Tiếng Việt

Home

Install Next.js 16 and create your first project

From an empty folder to a page running on your machine takes about a minute: install Node.js, run one command, then npm run dev. This page records exactly what create-next-app 16.4.4 wrote today — the six source files below are byte-identical to that run — plus what the docs leave out: next lint is gone, Tailwind v4 plugs into Turbopack, and a brand-new project already reports 16 moderate advisories.

The code on this page is an app that runs: 16/16 checks passed on 2026-11-02. Versions run: next@16.4.4, react@19.3.0, react-dom@19.3.0, typescript@5.9.3, eslint@9.39.5, tailwindcss@4.3.3, Node.js v26.0.0.

Before you start: Node.js 20.9 or newer

The Next.js docs require Node.js 20.9 at minimum, on macOS, Windows (including WSL) or Linux. The next package in a project declares the same thing in engines (>=20.9.0), so node -v tells you whether your machine can run it. The machine that proved this page ran Node.js v26.0.0.

You do not install Next.js globally: npx downloads create-next-app and runs it once, and npm comes with Node.js. On pnpm, yarn or Bun, swap npx for that tool's command and change --use-npm to --use-pnpm, --use-yarn or --use-bun; the other flags stay the same.

One command, no questions

The command below is one line and answers every prompt, so it runs in CI or a script without waiting for you to press Enter. Copy it whole, including the quotes around '@/*': npx create-next-app@16.4.4 my-app --ts --app --tailwind --eslint --use-npm --import-alias '@/*' --no-src-dir --disable-git --yes.

--yes answers the remaining questions with saved preferences or defaults. The docs give the recommended defaults: TypeScript, ESLint, Tailwind CSS, App Router, Cache Components, no src/ directory, and AGENTS.md; Turbopack is the default bundler. A --no-… flag negates a default, for example --no-tailwind, --no-linter, --no-cache-components.

One measured point the docs only hint at: create-next-app saves your choices for next time, and --yes may reuse them — a later run without --eslint still got the ESLint config from the run before. So this page passes every flag instead of trusting --yes. --reset-preferences does not rescue a script either: it asks “Would you like to reset the saved preferences?” before doing anything.

The tool also runs git init and makes the first commit, unless you pass --disable-git. Timing measured here: 8 seconds with a warm npm cache, about 46 seconds when packages had to download.

What the tool writes

An app-tw project with 18 files outside node_modules: app/layout.tsx, app/page.tsx, app/globals.css, app/favicon.ico, next.config.ts, eslint.config.mjs, tsconfig.json, next-env.d.ts, package.json, AGENTS.md, README.md, five SVGs in public/, .gitignore and the lockfile of the package manager you picked.

The two files below are the ones you edit daily. layout.tsx declares <html>, <body>, the fonts and the metadata; page.tsx is the content of /. The LayoutProps<"/"> type is not yours to write: next dev and next build generate a type per route under .next/types, and tsconfig.json includes that folder.

The six source and config files printed here were compared byte for byte with what create-next-app@16.4.4 wrote in today's run — not a hand copy tidied afterwards.

app/layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({ children }: LayoutProps<"/">) {
  return (
    <html
      lang="en"
      className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
    >
      <body className="min-h-full flex flex-col">{children}</body>
    </html>
  );
}
app/page.tsx
import Image from "next/image";

export default function Home() {
  return (
    <div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
      <main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
        <Image
          className="dark:invert h-5 w-[100px]"
          src="/next.svg"
          alt="Next.js logo"
          width={100}
          height={20}
          priority
        />
        <div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
          <h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
            To get started, edit the{" "}
            <code className="rounded bg-black/[.06] px-1.5 py-0.5 font-mono text-[0.9em] dark:bg-white/[.08]">
              page.tsx
            </code>{" "}
            file.
          </h1>
          <p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
            Looking for a starting point or more instructions? Head over to{" "}
            <a
              href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
              className="font-medium text-zinc-950 dark:text-zinc-50"
            >
              Templates
            </a>{" "}
            or the{" "}
            <a
              href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
              className="font-medium text-zinc-950 dark:text-zinc-50"
            >
              Learning
            </a>{" "}
            center.
          </p>
        </div>
        <div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
          <a
            className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
            href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            <Image
              className="dark:invert h-[14px] w-4"
              src="/vercel.svg"
              alt="Vercel logomark"
              width={16}
              height={14}
            />
            Deploy Now
          </a>
          <a
            className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
            href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            Documentation
          </a>
        </div>
      </main>
    </div>
  );
}

The 16.4.4 defaults

next.config.ts turns on cacheComponents and partialPrefetching by default, and plugs Tailwind CSS v4 into Turbopack with a loader rule. There is no postcss.config.mjs and no tailwind.config.js: Tailwind v4 reads @import "tailwindcss" in app/globals.css, and the theme is declared with @theme in the CSS itself.

tsconfig.json enables strict, adds the next plugin and maps @/* to the project root. The tool writes it; you change nothing to run. Swap --no-src-dir for --src-dir and the code lands in src/app/ with the alias following it.

next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
  cacheComponents: true,
  partialPrefetching: true,
  turbopack: {
    rules: {
      "*.css": {
        loaders: ["@tailwindcss/turbopack"],
        as: "*.css",
      },
    },
  },
};

export default nextConfig;
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "react-jsx",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./*"]
    }
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    ".next/types/**/*.ts",
    ".next/dev/types/**/*.ts",
    "**/*.mts"
  ],
  "exclude": ["node_modules"]
}

Run it: dev, build, start

cd my-app && npm run dev opens http://localhost:3000, and the browser updates as you edit. For production run npm run build, then npm run start. Next.js 16 uses Turbopack for both; add --webpack to use Webpack instead.

The new project's / reads nothing from the request, so it is rendered once at build time — checked in prerender-manifest.json. The browser still loads the React and Next.js runtime: measured at 178 KB gzipped for one page. That is the App Router's floor, not the starter page being heavy.

The Geist fonts declared in layout.tsx are fetched once at build and then self-hosted: 11 .woff2 files under .next/static/media, and the HTML points at no fonts.gstatic.com. The practical consequence: the build needs network (or a cache) to fetch the font; the visitor never calls Google.

Linting: `next lint` is gone in Next.js 16

The generated lint script is eslint, not next lint. Running next lint on 16.4.4 treats lint as a directory name, prints “Invalid project directory provided, no such directory: …/lint” and exits 1. The docs confirm that from Next.js 16, next build no longer runs the linter — you run it through a script.

npm run lint on the freshly generated project exits 0. If an older project still has next lint in package.json, change the script or run the codemod npx @next/codemod@canary next-lint-to-eslint-cli .

npm also warns that eslint@9.39.5 in the template is no longer supported. That is a tooling package, not your bug; upgrade ESLint when you are ready, and do not let it block creating the project.

A new project still reports security warnings

npm audit on the freshly created app reports 16 moderate advisories, 0 high, 0 critical. Most sit in the development toolchain (ESLint and its dependencies), not in code running on your server.

Do not run npm audit fix --force to make it go away: it can change the major version of a package you did not mean to upgrade. Read the list, upgrade what can be upgraded, and write down what is left.

The immediate task is knowing which version you run: npx next --version, or paste your site's address into our checker, then compare with the Next.js advisory list linked below.

Adding Next.js to an existing project

If you would rather not use the template: npm i next@latest react@latest react-dom@latest, add the four scripts dev, build, start, lint, then create app/layout.tsx (required, with <html> and <body>) and app/page.tsx. When you rename a file to .tsx, next dev creates tsconfig.json, next-env.d.ts and installs @types/*; TypeScript needs 5.1 or newer per the docs.

Two easy things to forget: .next, node_modules and next-env.d.ts belong in .gitignore (the template has them), and next-env.d.ts is never edited by hand — it is regenerated on every run.

Moving from the Pages Router to the App Router is an upgrade, not an install: read the Next.js 16 upgrade guide below before you change folder structure.

Checked

  • create-next-app with the page's flags, no prompt: exit 0 in 8s, Success!
  • the files the page prints are the files create-next-app writes: 6/6 identical
  • public/ still carries the five starter SVGs: 5/5
  • same dependencies, so the page's project matches the tool: @tailwindcss/turbopack @types/node @types/react @types/react-dom eslint eslint-config-next next react react-dom tailwindcss typescript
  • the version create-next-app puts in package.json: 16.4.4
  • scripts: dev, build, start, lint: next dev next build next start eslint
  • Node.js against next's engines field (>=20.9.0): v26.0.0, ok
  • next --version: Next.js v16.4.4
  • next build: / is prerendered: build time
  • GET / with no JavaScript: the starter heading is in the HTML: 200, present
  • public/ is served as static files: 200 image/svg+xml
  • next/font: Geist self-hosted at build (woff2 in .next/static/media), no fonts.gstatic.com in the HTML: 11 woff2, 0 external
  • JavaScript the browser loads for / (gzip): 178 KB in 7 files
  • `next lint` on 16: exit 1: Invalid project directory provided, no such directory: <project>/lint
  • npm run lint (eslint on the generated code): exit 0
  • npm audit on the freshly generated app: 16 total: 0 critical, 0 high, 16 moderate, 0 low

Re-run the checks: bun scripts/guides/verify-install.ts

Related

Sources

Our summary and wording; the code is rewritten from the examples in the Next.js docs. Where they differ, the Next.js docs win.