การตั้งค่าโปรเจกต์และ Toolchain
ไอเดียหลักในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียหลักในหนึ่งประโยค”การเริ่มทำงานกับ Next.js ให้ลื่นส่วนใหญ่คือ command เดียวสำหรับ scaffold, command เดียวสำหรับรัน และรู้ว่าไฟล์ไหนที่ framework มองเป็น convention ไม่ใช่โค้ดธรรมดา
scaffold แล้วรัน
หัวข้อที่มีชื่อว่า “scaffold แล้วรัน”สร้างโปรเจกต์และสตาร์ท dev server ด้วยสอง command —
npx create-next-app@latest my-appcd my-appnpm run devcreate-next-app จะถามว่าจะใช้ recommended defaults ไหม — TypeScript, ESLint, Tailwind CSS, App Router และไฟล์ AGENTS.md — หรือจะ customize เอง การเลือก defaults คือทางที่เร็วสุดและตรงกับทุกอย่างในคอร์สนี้
script ใน package.json คือชุดที่คุณจะรันทุกวัน —
{ "scripts": { "dev": "next dev", "build": "next build", "start": "next start" }}next dev รัน local development server พร้อม hot reloading ส่วน next build สร้าง output สำหรับ production ที่ optimize แล้ว และ next start เสิร์ฟ build นั้น
Turbopack คือ bundler เริ่มต้น
หัวข้อที่มีชื่อว่า “Turbopack คือ bundler เริ่มต้น”ตั้งแต่ Next.js 16 Turbopack เป็น bundler เริ่มต้นสำหรับทั้ง next dev และ next build — คุณไม่ต้อง opt in เพราะ Turbopack รันอยู่แล้ว เป็นทายาทของ webpack ที่เขียนด้วย Rust ออกแบบมาเพื่อ cold start ที่เร็วและ update แบบ incremental
คุณ config Turbopack ได้ตอนที่ต้องการ ผ่าน key turbopack ระดับบนสุดในไฟล์ config —
import type { NextConfig } from 'next';
const nextConfig: NextConfig = { turbopack: { // Turbopack-specific options go here },};
export default nextConfig;TypeScript และการอ่าน convention
หัวข้อที่มีชื่อว่า “TypeScript และการอ่าน convention”Next.js รองรับ TypeScript แบบ first-class ไฟล์ config เองก็เป็น next.config.ts ที่ type ด้วย NextConfig และ framework จะ generate next-env.d.ts ที่คุณไม่ควรแก้ TypeScript เปิดใช้งานโดย default เมื่อใช้ setup ที่แนะนำ
นิสัยที่ทำให้ Next.js เข้าใจง่ายขึ้นคือการหัด อ่าน convention ของตัวเอง ชื่อจำนวนหนึ่งถูกจองไว้และมีความหมายทุกที่ใต้ app/ —
- ชื่อไฟล์อย่าง
page,layout,loading,errorและrouteคือพฤติกรรม ไม่ใช่ module ทั่ว ๆ ไป - โฟลเดอร์คือ route segment ส่วนการครอบชื่อโฟลเดอร์ด้วยวงเล็บเหลี่ยม อย่าง
[slug]ทำให้ segment นั้นเป็น dynamic next.config.tsคือที่เดียวสำหรับเปลี่ยนพฤติกรรมระดับ framework
พอคุณมอง tree แล้วเดา URL และจุดที่โค้ดรันได้ framework ก็เลิกรู้สึกเหมือนเวทมนตร์ —
my-app/ app/ layout.tsx page.tsx public/ # static assets served as-is next.config.ts # framework configuration tsconfig.json # TypeScript configurationESLint หรือ Biome — คำถามเรื่อง linter
หัวข้อที่มีชื่อว่า “ESLint หรือ Biome — คำถามเรื่อง linter”ขั้นตอน customize ของ create-next-app ถาม linter ตรง ๆ — “Which linter would you like to use? ESLint / Biome / None” ESLint คือตัวเลือกดั้งเดิม มี rule เฉพาะ Next.js จาก @next/eslint-plugin-next; Biome คือ linter+formatter ตัวเดียวจบที่เร็ว รองรับ Next.js/React มาในตัว; None ข้าม linting ไปเลย เพิ่มทีหลังได้ ตั้งแต่ Next.js 16 ทั้ง next lint และ option eslint ในไฟล์ config ถูกถอดออกทั้งคู่ — ESLint ต้องต่อสายเองด้วยการติดตั้ง eslint + eslint-config-next แล้วเขียน flat config (eslint.config.mjs) เอง รันผ่าน ESLint CLI ตรง ๆ —
import { defineConfig, globalIgnores } from 'eslint/config'import nextVitals from 'eslint-config-next/core-web-vitals'
const eslintConfig = defineConfig([ ...nextVitals, globalIgnores(['.next/**', 'out/**', 'build/**', 'next-env.d.ts']),])
export default eslintConfignpx eslint .React Compiler
หัวข้อที่มีชื่อว่า “React Compiler”React Compiler (reactCompiler: true ใน next.config.ts) memoize component ให้อัตโนมัติ ลดการเขียน useMemo/useCallback เองส่วนใหญ่ ใน Next.js 16.3 มันไม่เปิดเองลอย ๆ ได้ — มันรันผ่าน Babel plugin ที่ Next.js ไม่ได้ bundle มาให้ —
// next.config.ts — variant reactCompilerimport type { NextConfig } from 'next';
const nextConfig: NextConfig = { reactCompiler: true,};
export default nextConfig;เปิดตัวนี้โดยไม่ติดตั้ง plugin ทำให้ build fail จริง พิสูจน์แล้วใน probe —
Error: Turbopack build failed with 1 error:./next.config.tsError: Failed to resolve package babel-plugin-react-compiler while attempting to resolve React CompilerReact compiler is enabled in next.config.ts. We attempted to resolve React Compiler relative to the next package. Is babel-plugin-react-compiler installed in your node_modules directory?การติดตั้ง dependency ที่ขาดไปคือทางแก้ทั้งหมด — npm install -D babel-plugin-react-compiler แล้ว next.config.ts ตัวเดิมเป๊ะก็ build ผ่านสะอาด create-next-app เองก็มี flag --react-compiler ตรง ๆ (และ prompt ในขั้น customize) ที่ตั้งค่าให้พร้อม plugin เลย
Typed routes กับ next typegen
หัวข้อที่มีชื่อว่า “Typed routes กับ next typegen”typedRoutes: true ทำให้ <Link href> (และ push/replace/prefetch ของ next/navigation) ถูก type-check กับ route จริงของ app คุณ — string literal ที่ไม่ใช่ route จริงกลายเป็น type error ไม่ใช่ 404 ที่เจอตอน runtime —
// next.config.ts — variant typedRoutesimport type { NextConfig } from 'next';
const nextConfig: NextConfig = { typedRoutes: true,};
export default nextConfig;แต่ก่อน route type จะถูก generate เป็นผลพลอยได้จาก next dev/next build เท่านั้น ตอนนี้มีคำสั่ง next typegen แยกต่างหาก (stable ตั้งแต่ v15.5) ที่ generate มันเองได้ ซึ่งสำคัญสำหรับ type-check ใน CI โดยไม่ต้อง build เต็ม —
npx next typegen && npx tsc --noEmitพิสูจน์แล้วใน probe ด้วย route ที่ไม่มีจริง —
import Link from 'next/link'
export default function Home() { return ( <div> <Link href="/typed-a">Good link</Link> <Link href="/nope-not-a-route">Bad link</Link> </div> )}app/page.tsx(7,13): error TS2322: Type '"/nope-not-a-route"' is not assignable to type 'UrlObject | RouteImpl<"/nope-not-a-route">'.next typegen พิมพ์ ✓ Types generated successfully และหลังจากนั้นเท่านั้นที่ tsc --noEmit จะมี generated route type ให้ใช้จับ typo ได้
scaffold AGENTS.md / CLAUDE.md
หัวข้อที่มีชื่อว่า “scaffold AGENTS.md / CLAUDE.md”recommended defaults (และ flag เดี่ยว --agents-md) ยังเขียน AGENTS.md ไว้ที่ root โปรเจกต์ ควบคู่กับ CLAUDE.md ที่แค่ re-export มันอีกที (@AGENTS.md) เพื่อให้ Claude Code รับ rule ชุดเดียวกัน จุดประสงค์ของมันตรงไปตรงมา — training data ของคุณล้าสมัยทันทีที่ major version ใหม่ออก —
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure mayall differ from your training data. Read the relevant guide in`node_modules/next/dist/docs/` before writing any code. Heed deprecationnotices.
This block is written and re-added by `next dev` — removing it from a diffonly re-creates the uncommitted change; committing it with your work keepsthe tree clean.
<!-- END:nextjs-agent-rules -->จุดสุดท้ายสำคัญมากสำหรับ agent ที่ทำงานใน repo นี้เอง — next dev generate block นี้กลับมาเองทุกครั้ง การลบมันจึงไม่ใช่การแก้ที่ถาวร — มันจะกลับมาอีก
flowchart LR Create[create-next-app] --> Tree[app/ structure] Tree --> Dev[next dev] Dev --> Turbo[Turbopack bundles] Turbo --> Browser[Live app in the browser] Config[next.config.ts] --> Turbo
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”create-next-app ไม่ใช่ “เริ่มจากโฟลเดอร์เปล่า ๆ” — มันคือการเลือกจากชุด toggle ที่ document ไว้ตายตัว (TypeScript, linter, Tailwind, src/, import alias, React Compiler, AGENTS.md) และทุก toggle มีผลจริงที่เช็คได้กับ tree ที่ generate ออกมา มองทั้ง toolchain แบบเดียวกัน — config key อย่าง reactCompiler หรือ typedRoutes ไม่ใช่ “เวทมนตร์ที่ทำงานหรือไม่ทำงาน” — มันคือ feature ที่ document ไว้ มี dependency ของตัวเอง และมี error message จริงเมื่อ dependency นั้นขาดไป
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ได้อะไร | ประโยชน์ | ต้นทุน |
|---|---|---|
recommended defaults ของ create-next-app | TypeScript/ESLint/App Router ต่อสายถูกต้องในคำสั่งเดียว | มีความเห็นในตัว — customize ทีหลังต้อง undo default บางตัว |
| ESLint (flat config) | ecosystem อยู่ตัว ตัวอย่าง Next.js ส่วนใหญ่สมมติว่าใช้ตัวนี้ | ต้องเขียนและดูแล eslint.config.mjs เองเพราะ next lint หายไปแล้ว |
| Biome | binary เดียวเร็ว แทน ESLint+Prettier | plugin ecosystem เล็กกว่า ESLint |
| React Compiler | เขียน useMemo/useCallback เองน้อยลง | dependency เพิ่ม (babel-plugin-react-compiler) และ build time เพิ่มนิดหน่อย |
typedRoutes | link ที่พังกลายเป็น type error ไม่ใช่ 404 | ต้อง next typegen (หรือ dev/build) generate type ใหม่ก่อน tsc จะเห็น |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- “
reactCompiler: trueแค่ใส่ config key ก็ใช้ได้เลย” มันต้องการbabel-plugin-react-compilerติดตั้งอยู่ด้วย — ถ้าไม่มีnext buildจะ fail ทันทีด้วย error จริงที่บอกชื่อ ไม่ใช่ no-op เงียบ ๆ - “
next lintยังมีอยู่ใน Next.js 16” มันถูกถอดออกใน v16.0.0 พร้อมกับ optioneslintในnext.config.js— ตอนนี้ ESLint รันผ่าน ESLint CLI ธรรมดากับ flat config ของคุณเอง - “
tsc --noEmitเฉย ๆ จับ typed-route ผิดพลาดได้ทุกกรณี” route type มาจาก.next/typesที่ generate โดยnext dev,next build, หรือnext typegen— รันtscเปล่า ๆ ใน checkout ใหม่ (หรือ CI) โดยไม่มีสามอย่างนี้มาก่อนแปลว่า route type ยังไม่มี - “block
AGENTS.mdเป็นแค่คำแนะนำ ลบได้”next devเติมมันกลับมาเอง — ลบออกจาก diff โดยไม่แก้ต้นตอแค่สร้าง uncommitted change ใหม่ตอนรันdevครั้งถัดไป
อ่านโค้ดที่ AI เขียน
หัวข้อที่มีชื่อว่า “อ่านโค้ดที่ AI เขียน”เวลาโค้ดที่ generate มาแตะ next.config.ts หรือ scaffold ของโปรเจกต์ เช็คสิ่งเหล่านี้ก่อนเชื่อ —
- มันตั้ง
reactCompiler: trueโดยไม่พูดถึงการติดตั้งbabel-plugin-react-compilerเลยหรือเปล่า? build จะ fail ด้วย error “Failed to resolve package” ที่ชัดเจนตั้งแต่รันครั้งแรก - มันใช้
next lintหรือ keyeslintในnext.config.jsหรือเปล่า? ทั้งคู่ถูกถอดออกใน Next.js 16 แล้ว — โค้ดปัจจุบันใช้ ESLint CLI กับ flat configeslint.config.mjs - มันสมมติว่า
<Link href="...">type-check กับ route จริงโดยไม่มีtypedRoutes: trueใน config และไม่มีการรันnext typegen/dev/buildเพื่อ generate type หรือเปล่า? - มันอ้างถึง
getInitialProps,next.config.jsแบบmodule.exports, หรือ pattern อื่นที่เก่ากว่าnext.config.tsแบบ typed, ESM-friendly ปัจจุบันหรือเปล่า?
🐛 หา bug ในโค้ดนี้
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {reactCompiler: true,typedRoutes: true,}
export default nextConfigเฉลย
config นี้เป็นแค่ครึ่งเรื่อง — reactCompiler: true ต้องการ babel-plugin-react-compiler ติดตั้งเป็น dependency จริง ๆ ด้วย และ typedRoutes: true ต้องการรัน next typegen (หรือ dev/build) ก่อนที่ tsc จะเห็น route type ที่ generate มา พิสูจน์แล้วใน probe — ด้วย config เป๊ะแบบนี้และไม่มี babel-plugin-react-compiler ใน node_modules, next build fail ทันทีด้วย Error: Failed to resolve package babel-plugin-react-compiler while attempting to resolve React Compiler แค่ config key อย่างเดียวไม่เปลี่ยนอะไรเลย — dependency (และสำหรับ typed routes คือขั้น generate type) ต้องมีอยู่จริงด้วย