Setting Up Next.js 16 with TypeScript: A Modern Project Walkthrough
Spin up a fresh Next.js 16 project with TypeScript, Tailwind, and a sane folder layout — and understand the App Router conventions before you write your first feature.
Next.js 16 is opinionated in ways that pay off if you understand them up front and frustrating if you don't. This walkthrough takes a fresh terminal to a running TypeScript app with Tailwind, the App Router, and the conventions modern Next.js codebases actually use. We'll skip the marketing bullet points and stick to what's worth knowing on day one.
Prerequisites
- Node.js 18.18 or newer (20+ recommended)
- A package manager — pnpm, npm, or bun
- Familiarity with React
Create the project
pnpm create next-app@latest my-app
# Answer:
# - TypeScript? Yes
# - ESLint? Yes
# - Tailwind? Yes
# - src/ directory? Yes
# - App Router? Yes
# - Turbopack? Yes
# - Customize import alias? @/*
cd my-app
pnpm dev
What just got created
src/app/— the App Router. Every folder is a route segment.src/app/layout.tsx— the root layout that wraps every page.src/app/page.tsx— the home page (the URL/).next.config.ts— runtime configuration.tsconfig.json— strict TS, with the@/*import alias mapped tosrc/*.
Server Components by default
Every component you write in src/app/ is a Server Component unless you opt in to a Client Component with "use client" at the top. Server Components can fetch data directly without an API layer; Client Components are needed for state, effects, and event handlers.
// src/app/page.tsx — Server Component
async function getPosts() {
const res = await fetch("https://api.example.com/posts", { next: { revalidate: 60 } });
return res.json();
}
export default async function Home() {
const posts = await getPosts();
return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;
}
A sane folder layout
src/
app/ # routes
layout.tsx
page.tsx
blog/
page.tsx
[slug]/page.tsx
components/ # reusable UI
ui/ # shadcn/ui primitives
layout/ # header, footer
lib/ # framework-agnostic code
db/ # ORM models, queries
Type-safe environment variables
// src/lib/env.ts
import { z } from "zod";
const Env = z.object({
DATABASE_URL: z.string().url(),
NEXT_PUBLIC_SITE_URL: z.string().url(),
});
export const env = Env.parse(process.env);
Tailwind on day one
Tailwind v4 is included by default. Open src/app/globals.css and you'll see the new @import "tailwindcss"; directive plus a small theme block where you can define your tokens. Resist the urge to override everything — start with the defaults and customize only what hurts.
Lint and format
ESLint is wired up with next/core-web-vitals. Add Prettier and an editor integration so format-on-save works. We have a separate tutorial dedicated to the full TypeScript ESLint + Prettier setup.
Where to go next
- Add an ORM — Drizzle or Prisma sit cleanly inside Server Components.
- Wire up authentication with NextAuth (Auth.js v5).
- Deploy to Vercel —
vercelfrom the project root and you're live.
Continue reading
Related essays
Tailwind CSS v4 Project Setup: The Modern Configuration
Tailwind v4 changed how configuration works — no more <code>tailwind.config.js</code> by default. Here's the modern setup for any Vite or Next.js project, explained.
Supabase Setup for Authentication and Database
Wire up Supabase as your backend in under an hour — authentication, Postgres, row-level security, and a typed client. Real-world patterns for a Next.js app.
Getting Started with the Claude API: Build Your First App in 30 Minutes
Spin up a working chat app with the Claude API in half an hour — covering the SDK install, authentication, streaming responses, and the patterns you'll actually use in production.