Next.js 14 menjadi standar baru untuk aplikasi React modern. Tutorial ini memberi langkah demi langkah cara menginstal, mengkonfigurasi, menulis kode, dan menerapkan best practice keamanan serta performa pada proyek Next.js 14 di tahun 2026.

1. Prasyarat dan Persiapan Lingkungan

Pastikan Anda memiliki:

  • Node.js v20.10 atau lebih baru (download)
  • npm v10 atau Yarn v4 (pilih salah satu)
  • Git untuk version control
  • Editor kode modern, misalnya VS Code dengan ekstensi ESLint dan Prettier

Verifikasi instalasi dengan:

node -v
npm -v   # atau yarn -v

2. Membuat Proyek Next.js 14 Baru

Gunakan create-next-app yang sudah terintegrasi dengan versi terbaru:

npx create-next-app@latest my-next14-app --typescript --eslint --tailwind

Parameter yang dipakai:

  • --typescript: menyiapkan TypeScript out‑of‑the‑box.
  • --eslint: menambahkan konfigurasi linting standar.
  • --tailwind: menginstall Tailwind CSS v4 (rilis 2026) untuk styling cepat.

Setelah selesai, masuk ke direktori proyek:

cd my-next14-app

3. Struktur Direktori Utama di Next.js 14

Berikut layout yang penting untuk App Router:

app/
  layout.tsx          # Root layout, menampung  dan 
  page.tsx            # Landing page (route "/")
  dashboard/
    layout.tsx        # Layout khusus dashboard
    page.tsx          # Route "/dashboard"
    actions.ts        # Server Actions untuk dashboard
  api/
    route.ts          # API Route (edge atau node)
public/
  favicon.ico
  ...

Folder pages/ masih ada untuk backward compatibility, tetapi fokus pada app/ untuk proyek baru.

4. Instalasi Dependensi Tambahan

Berikut paket yang direkomendasikan pada Juni 2026:

npm i @tanstack/react-query@5 @headlessui/react@2 zod@3.23
npm i -D @types/node@20
# Jika menggunakan Yarn
# yarn add @tanstack/react-query@5 @headlessui/react@2 zod@3.23
# yarn add -D @types/node@20

Penjelasan singkat:

  • @tanstack/react-query: data fetching & caching yang terintegrasi dengan Server Actions.
  • @headlessui/react: komponen UI tanpa gaya, cocok dengan Tailwind.
  • zod: schema validation untuk input API dan Server Actions.

5. Konfigurasi TypeScript & ESLint

Edit tsconfig.json untuk menambahkan path alias “@/”.

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./*" ]
    },
    "strict": true,
    "moduleResolution": "node",
    "jsx": "preserve",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

Pastikan .eslintrc.json mengaktifkan eslint-plugin-next dan eslint-plugin-react-hooks:

{
  "extends": ["next/core-web-vitals", "plugin:react-hooks/recommended"],
  "rules": {
    "react/react-in-jsx-scope": "off",
    "@next/next/no-html-link-for-pages": "off"
  }
}

6. Membuat Layout Global dengan Server Components

File app/layout.tsx:

import '@/styles/globals.css';
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

export const metadata: Metadata = {
  title: 'Next.js 14 – Demo App',
  description: 'Contoh implementasi App Router, Server Actions, dan optimasi performa.',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    
      
      
        {children}
      
    
  );
}

Catatan: gunakan className dari Google Font untuk mengurangi FOUT (Flash Of Unstyled Text).

7. Membuat Server Action untuk Formulir Kontak

File app/contact/actions.ts (Server Action):

"use server";
import { z } from 'zod';
import { randomUUID } from 'crypto';
import { revalidatePath } from 'next/cache';

const ContactSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  message: z.string().min(10),
});

export async function submitContact(formData: FormData) {
  const result = ContactSchema.safeParse({
    name: formData.get('name'),
    email: formData.get('email'),
    message: formData.get('message'),
  });

  if (!result.success) {
    return { error: result.error.format() };
  }

  // Simulasi penyimpanan ke DB (contoh: Prisma di edge runtime)
  const id = randomUUID();
  // await prisma.contact.create({ data: { id, ...result.data } });

  // Revalidate cache jika ada halaman yang menampilkan daftar kontak
  revalidatePath('/contact/thanks');

  return { success: true, id };
}

Langkah selanjutnya, buat formulir di app/contact/page.tsx yang memanggil action tersebut.

8. Membuat Halaman Formulir dengan Client Component

"use client";
import { useState } from 'react';
import { submitContact } from './actions';

export default function ContactPage() {
  const [status, setStatus] = useState('');
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const formData = new FormData(e.target as HTMLFormElement);
    const res = await submitContact(formData);
    if (res.error) {
      setStatus('Error: ' + JSON.stringify(res.error));
    } else {
      setStatus('Message sent! ID: ' + res.id);
    }
  };

  return (
    

Contact Us