Next.js 14 menjadi standar de‑facto untuk pembangunan aplikasi React modern. Tutorial ini membimbing Anda langkah demi langkah dari instalasi hingga deploy, lengkap dengan contoh kode, konfigurasi, dan best practice untuk meningkatkan performa serta keamanan.

1. Prasyarat

Sebelum memulai, pastikan Anda telah menginstall:

  • Node.js v20.x atau lebih baru (download di nodejs.org)
  • npm v10.x atau Yarn v4 (pilih salah satu)
  • Git untuk version control
  • Editor kode seperti VS Code

2. Membuat Proyek Next.js 14 Baru

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

Flag --experimental-app mengaktifkan App Router yang menjadi default pada Next.js 14. Opsi --ts menyiapkan TypeScript secara otomatis.

3. Struktur Direktori Penting

  • app/ – tempat semua route berbasis folder (App Router)
  • components/ – UI reusable
  • lib/ – helper functions, API client, dll
  • public/ – aset statis

Next.js 14 menghilangkan pages/ secara default, jadi pastikan Anda menempatkan semua halaman di dalam app/.

4. Instalasi Dependensi Tambahan

# UI library (Tailwind CSS)
npx tailwindcss init -p
# Headless UI untuk komponen aksesibel
npm i @headlessui/react
# Tanstack Query untuk data fetching
npm i @tanstack/react-query
# Prisma sebagai ORM (opsional)
npm i prisma @prisma/client
# dotenv untuk environment variable
npm i dotenv

Jangan lupa menambahkan tailwind.config.js dan mengimport Tailwind di app/globals.css.

5. Konfigurasi Environment

# .env.local
NEXT_PUBLIC_API_URL=https://api.example.com
DATABASE_URL=postgresql://user:password@localhost:5432/mydb

Gunakan dotenv di next.config.mjs untuk memuat variabel:

import { config } from 'dotenv';
config();

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  experimental: { appDir: true },
  images: { remotePatterns: [{ hostname: 'images.example.com' }] },
};
export default nextConfig;

6. Membuat Route dengan Server Actions

Server Actions memungkinkan menjalankan kode server langsung dari komponen React tanpa API route terpisah.

// app/contact/page.tsx
'use client';
import { useState, FormEvent } from 'react';

export default function ContactPage() {
  const [status, setStatus] = useState('idle');

  async function sendMessage(formData: FormData) {
    'use server';
    // kode server: simpan ke DB atau kirim email
    const message = formData.get('message');
    // contoh: menyimpan lewat Prisma
    await prisma.contact.create({ data: { message: String(message) } });
    return 'success';
  }

  async function handleSubmit(e: FormEvent) {
    e.preventDefault();
    setStatus('loading');
    const formData = new FormData(e.currentTarget);
    const result = await sendMessage(formData);
    setStatus(result === 'success' ? 'sent' : 'error');
  }

  return (