Next.js 15 telah menjadi standar de‑facto untuk aplikasi React modern dengan fokus pada server‑side rendering, streaming, dan integrasi AI. Tutorial ini menuntun Anda step‑by‑step mulai dari instalasi, konfigurasi, contoh kode, hingga best practice untuk menjaga performa dan keamanan.

1. Persiapan Lingkungan

Pastikan Anda memiliki Node.js LTS terbaru (v20.10 atau lebih tinggi) dan npm atau pnpm versi 9+. Verifikasi dengan:

node -v
npm -v  # atau pnpm -v

Jika belum terpasang, unduh dari nodejs.org.

2. Membuat Project Next.js 15 Baru

Gunakan perintah resmi create-next-app dengan flag --ts untuk TypeScript dan --app untuk mengaktifkan App Router secara default.

npx create-next-app@latest my-next15-app --ts --app
cd my-next15-app

Struktur folder yang dihasilkan akan mencakup app/ (bukan pages/) serta src/ bila Anda memilih opsi itu.

3. Instalasi Dependensi Tambahan

Untuk fitur terbaru, tambahkan paket berikut:

# Manajemen state (optional but recommended)
pm install zustand
# Tailwind CSS untuk styling cepat
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
# React Hook Form untuk form handling yang terintegrasi dengan Server Actions
npm install react-hook-form
# ESLint & Prettier untuk standar kode
npm install -D eslint eslint-config-next prettier

Konfigurasi tailwind.config.js:

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

4. Mengaktifkan Server Actions

Next.js 15 memperkenalkan Server Actions yang memungkinkan Anda menulis fungsi server langsung di dalam komponen React tanpa API routes terpisah. Pastikan experimental flag di next.config.js di‑enable:

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: true,
  },
};
module.exports = nextConfig;

Setelah itu, Anda dapat menulis aksi seperti berikut di dalam app/contact/page.tsx:

"use server";
import { redirect } from "next/navigation";

export async function sendMessage(formData: FormData) {
  // Validasi sederhana
  const name = formData.get("name") as string;
  const email = formData.get("email") as string;
  const message = formData.get("message") as string;

  if (!name || !email || !message) {
    throw new Error("All fields are required");
  }

  // Simulasi penyimpanan ke DB (replace dengan Prisma dll.)
  await new Promise((res) => setTimeout(res, 500));

  // Setelah berhasil, redirect ke halaman terima kasih
  redirect("/thank-you");
}

Komponen React yang memanggil aksi ini:

import { useForm } from "react-hook-form";
import { sendMessage } from "./page";

export default function ContactForm() {
  const { register, handleSubmit } = useForm();

  const onSubmit = async (data: any) => {
    const formData = new FormData();
    Object.entries(data).forEach(([key, value]) =>
      formData.append(key, value as string)
    );
    await sendMessage(formData);
  };

  return (