Next.js 14 telah menjadi standar de‑facto untuk membangun aplikasi web React modern. Artikel ini memberikan tutorial step‑by‑step yang akurat dan up‑to‑date (Mei 2026) untuk meng‑setup proyek, mengkonfigurasi lingkungan, menulis kode contoh, serta menerapkan best practice performance dan security.

1. Prasyarat dan Persiapan Lingkungan

Sebelum memulai, pastikan Anda memiliki:

  • Node.js v20.12 atau lebih baru (unduh di nodejs.org)
  • npm v10 atau Yarn v4 (opsional, pilih satu)
  • Git untuk version control
  • Editor kode, rekomendasi VS Code dengan ekstensi Next.js dan Tailwind CSS IntelliSense

Instalasi Node.js dan npm

# Debian/Ubuntu
sudo apt-get update && sudo apt-get install -y curl gnupg
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

# Verifikasi
node -v   # => v20.12.0
npm -v    # => 10.x.x

2. Membuat Project Next.js 14

Gunakan create‑next‑app dengan flag terbaru

Next.js 14 memperkenalkan app router secara default. Jalankan perintah berikut untuk membuat proyek baru:

npx create-next-app@latest my-next14-app \
  --ts \               # TypeScript (opsional tetapi disarankan)
  --app \              # Mengaktifkan app router
  --eslint \          # Konfigurasi ESLint bawaan
  --src-dir            # Menyimpan kode di folder src

Perintah di atas menghasilkan struktur folder modern:

my-next14-app/
├─ src/
│  ├─ app/            # App Router root
│  ├─ pages/          # Legacy pages (bisa dihapus)
│  ├─ components/
│  └─ styles/
├─ public/
├─ next.config.mjs
└─ package.json

Instalasi Dependensi Tambahan

Untuk contoh tutorial, tambahkan Tailwind CSS, Prisma (ORM) dan React Hook Form:

# Tailwind CSS + autoprefixer + postcss
npm i -D tailwindcss@latest postcss@latest autoprefixer@latest

# Prisma (DB layer)
npm i prisma @prisma/client
npx prisma init --datasource-provider sqlite

# React Hook Form (form handling)
npm i react-hook-form

3. Konfigurasi Tailwind CSS

Jalankan init dan edit file konfigurasi:

npx tailwindcss init -p

Ubah tailwind.config.ts menjadi:

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./src/**/*.{js,ts,jsx,tsx}",
    "./app/**/*.{js,ts,jsx,tsx}"
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

Tambahkan Tailwind directives ke src/styles/globals.css:

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

4. Menyiapkan Database dengan Prisma

Ubah prisma/schema.prisma untuk SQLite contoh sederhana:

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

generator client {
  provider = "prisma-client-js"
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  createdAt DateTime @default(now())
}

Jalankan migrasi:

npx prisma migrate dev --name init

Setelah selesai, Prisma Client siap dipanggil dari kode server.

5. Membuat Route dengan App Router & Server Component

Folder src/app Struktur

src/app/
├─ layout.tsx          # Root layout
├─ page.tsx            # Home page (Server Component)
├─ posts/
│  ├─ page.tsx        # List posts (Server Component)
│  └─ new/
│     └─ page.tsx     # Form create (Client Component)

Root Layout (src/app/layout.tsx)

import "../styles/globals.css";
import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "Next.js 14 Demo",
  description: "Demo aplikasi dengan App Router, Server Components, dan Prisma",
};

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

Home Page (src/app/page.tsx) – Server Component

import Link from "next/link";

export default function Home() {
  return (
    

Selamat Datang di Next.js 14

Aplikasi contoh ini menunjukkan:

  • App Router dengan folder app
  • Server Component untuk fetching data
  • Client Component untuk formulir
  • Prisma sebagai ORM
Lihat daftar posting →
); }

List Posting (src/app/posts/page.tsx)

import { prisma } from "@/lib/prisma";
import Link from "next/link";

// Server Component, langsung fetch data
export const dynamic = "force-dynamic"; // untuk dev, fetch tiap request

export default async function PostsPage() {
  const posts = await prisma.post.findMany({ orderBy: { createdAt: "desc" } });

  return (
    

Daftar Posting

Buat Posting Baru {posts.length === 0 ? (

Belum ada posting.

) : (
    {posts.map((post) => (
  • {post.title}

    {post.content?.slice(0, 100)}…

  • ))}
)}
); }

Formulir Buat Posting (Client Component)

Karena kita memerlukan state di browser, buat file src/app/posts/new/page.tsx sebagai Client Component:

"use client";
import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";

type FormData = {
  title: string;
  content: string;
};

export default function NewPost() {
  const { register, handleSubmit, reset, formState: { errors, isSubmitting } } = useForm();
  const router = useRouter();

  const onSubmit = async (data: FormData) => {
    const res = await fetch("/api/posts", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data),
    });
    if (res.ok) {
      reset();
      router.push("/posts");
    }
  };

  return (
    

Buat Posting Baru

{errors.title &&

Judul wajib diisi

}