Next.js 14 menjadi standar de‑facto untuk aplikasi React modern. Tutorial ini menjelaskan cara menginstal, mengkonfigurasi, dan mengoptimalkan proyek Next.js 14 dengan App Router, Server Actions, dan best practice security serta performance.

1. Prasyarat dan Persiapan Lingkungan

Sebelum memulai, pastikan Anda memiliki:

  • Node.js v20.10 atau lebih baru (download di nodejs.org)
  • npm v10 atau Yarn v2+ (pilih salah satu)
  • Git untuk version control
  • Editor kode – VS Code direkomendasikan dengan ekstensi ESLint, Prettier, dan Tailwind CSS IntelliSense

2. Membuat Proyek Next.js 14 Baru

npx create-next-app@latest my-next14-app --experimental-app-router --ts
cd my-next14-app

Flag --experimental-app-router otomatis menyiapkan struktur app/ (App Router) dan TypeScript. Jika Anda lebih suka JavaScript, hilangkan --ts.

2.1. Memeriksa Versi

node -v   # harus >=20.10
npm -v    # atau yarn -v

Jika versi tidak sesuai, upgrade dengan nvm install 20 && nvm use 20.

3. Instalasi Dependency Utama

Kami akan menambahkan beberapa paket yang menjadi praktik umum pada 2026:

  • tailwindcss@^3.4 – utility‑first CSS
  • @auth0/nextjs-auth0@^4.2 – otentikasi OAuth
  • zod@^3.23 – schema validation untuk Server Actions
  • next‑secure‑headers@^2.0 – header security default
# dengan npm
npm i -D tailwindcss postcss autoprefixer
npm i @auth0/nextjs-auth0 zod next-secure-headers

# atau dengan Yarn
yarn add -D tailwindcss postcss autoprefixer
yarn add @auth0/nextjs-auth0 zod next-secure-headers

4. Konfigurasi Tailwind CSS

npx tailwindcss init -p

File tailwind.config.js otomatis dibuat. Ubah content agar mencakup folder app:

module.exports = {
  content: [
    "./app/**/*.{js,ts,jsx,tsx}",
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}"
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

Tambahkan directive Tailwind ke app/globals.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

5. Mengamankan Aplikasi dengan next‑secure‑headers

Buat file middleware.ts di root proyek:

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { secureHeaders } from 'next-secure-headers';

export function middleware(request: NextRequest) {
  const response = NextResponse.next();
  response.headers.set('Content-Security-Policy', secureHeaders().contentSecurityPolicy);
  // tambahkan header lain secara otomatis
  Object.entries(secureHeaders()).forEach(([key, value]) => {
    response.headers.set(key, value as string);
  });
  return response;
}

export const config = {
  matcher: '/:path*', // berlaku pada semua route
};

Middleware akan dijalankan pada setiap request, menambahkan header security terbaru (HSTS, X‑Content‑Type‑Options, dll).

6. Membuat API Route dengan Server Actions

Server Actions memungkinkan Anda mengeksekusi fungsi server langsung dari komponen React tanpa menulis API route terpisah.

6.1. Definisikan Action

// app/actions/contact.ts
import { z } from 'zod';
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 parsed = ContactSchema.safeParse({
    name: formData.get('name'),
    email: formData.get('email'),
    message: formData.get('message'),
  });

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

  // Simulasi penyimpanan ke DB atau panggilan webhook
  await new Promise(r => setTimeout(r, 500));

  // Re‑validasi halaman bila diperlukan
  revalidatePath('/contact');
  return { success: true };
}

6.2. Menggunakan Action di Komponen

// app/contact/page.tsx
'use client';
import { useState } from 'react';
import { submitContact } from '@/app/actions/contact';

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

  return (
    

Contact Us