Web Dev 21 Aug 2026 4 views 0 komentar

Belajar Next.js untuk Pemula - App Router dan React Server Components

Belajar Next.js untuk Pemula - App Router dan React Server Components

Dulu saya termasuk developer yang ogah-ogahan pakai framework React. "React + Vite saja cukup," kata saya waktu itu. Routing pakai react-router-dom, SEO pakai react-helmet, data fetching pakai react-query. Semua dirakit manual. Berhasil sih, tapi tiap bikin project baru, setup-nya ngulang dari nol. Sampai suatu hari klien minta website yang muncul di Google, dan saya harus ngurus SSR, meta tag, sitemap, semuanya sendirian. Saat itu saya menyerah dan coba Next.js. Ternyata... semudah itu.

Next.js adalah framework React paling populer di dunia saat ini. Dipakai oleh Vercel, TikTok, Netflix, sampai Notion. Di artikel ini saya ajak kamu belajar dari nol: install, paham struktur folder, bikin halaman, sampai deploy ke VPS. Semua pakai contoh kode yang bisa langsung kamu praktikkan.

Kenapa Next.js Bukan Sekadar "React yang Ditingkatkan"

React itu library. Dia cuma ngurus UI. Routing, SSR, SEO, bundling, image optimization - semua harus kamu setup sendiri. Next.js adalah framework yang membungkus React dengan solusi built-in untuk semua hal itu. Kamu dapat file-based routing, server components, API routes, dan optimasi gambar tanpa install puluhan package.

  • File-based routing: bikin file = bikin halaman. Tidak perlu konfigurasi router tambahan.
  • Server Components: render React di server, kirim HTML ke browser. JavaScript di client jadi jauh lebih ringan.
  • SSR, SSG, ISR: pilih strategi render per halaman. Mau static, dynamic, atau campuran.
  • Built-in SEO: Metadata API, sitemap, robots.txt, semuanya dari kode.

Intinya: kamu fokus nulis fitur, Next.js yang ngurus sisanya.

Persiapan: Node.js dan create-next-app

Sebelum mulai, pastikan Node.js versi 18.18 atau lebih baru sudah terinstall. Cek dengan:


node -v
npm -v

Kalau belum ada, install dulu dari nodejs.org atau pakai nvm. Setelah itu, buat project baru:


npx create-next-app@latest blog-saya

Perintah tadi akan nanya beberapa hal: TypeScript atau JavaScript, ESLint, Tailwind CSS, App Router, dan import alias. Untuk belajar, pilih TypeScript: Yes, App Router: Yes, sisanya terserah kamu. Kalau bingung, pilih default saja.

Setelah selesai, masuk ke folder dan jalankan:


cd blog-saya
npm run dev

Buka http://localhost:3000 - website pertamamu sudah jalan.

Memahami Struktur Folder App Router

Next.js versi modern (13+) pakai App Router. Semua halaman hidup di folder app/. Strukturnya kira-kira begini:


blog-saya/
|-- app/
|   |-- layout.tsx
|   |-- page.tsx
|   |-- globals.css
|   `-- about/
|       `-- page.tsx
|-- public/
|-- package.json
`-- next.config.ts

Aturannya sederhana: page.tsx adalah halaman, layout.tsx adalah kerangka yang membungkus halaman-halaman di bawahnya. File app/about/page.tsx otomatis jadi route /about. Tidak ada konfigurasi router tambahan.

Membuat Halaman dan Layout Pertama

Buka app/page.tsx dan ganti isinya:


export default function Home() {
  return (
    <main>
      <h1>Halo, Next.js!</h1>
      <p>Ini halaman pertama saya.</p>
    </main>
  );
}

Simpan, dan halaman http://localhost:3000 langsung berubah. Sekarang bikin halaman About. Buat folder app/about/ lalu tambahkan page.tsx di dalamnya. Isi dengan konten apa saja, lalu akses /about. Halaman baru muncul tanpa restart server.

Layout di app/layout.tsx dipakai untuk elemen yang sama di semua halaman - navbar, footer, atau metadata global:


import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "Blog Saya",
  description: "Tempat saya menulis tentang web development",
};

export default function RootLayout({
  children,
}: Readonly<{ children: React.ReactNode }>) {
  return (
    <html lang="id">
      <body>
        <nav>Menu: Beranda | About</nav>
        {children}
        <footer>Copyright 2026</footer>
      </body>
    </html>
  );
}

Navigasi Antar Halaman

Jangan pakai tag <a> biasa untuk link internal. Pakai Link dari Next.js supaya navigasi cepat (client-side transition) dan tidak reload penuh:


import Link from "next/link";

export default function Navbar() {
  return (
    <nav>
      <Link href="/">Beranda</Link>
      <Link href="/about">About</Link>
    </nav>
  );
}

Untuk halaman dinamis seperti detail artikel, buat folder dengan nama dalam kurung siku:


app/
`-- blog/
    |-- page.tsx
    `-- [slug]/
        `-- page.tsx

Route /blog/[slug] menangkap URL seperti /blog/belajar-nextjs. Nilai slug bisa diakses lewat parameter params:


export default async function BlogPost({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  return <h1>Artikel: {slug}</h1>;
}

Catatan: di Next.js 15, params dan searchParams berupa Promise, jadi perlu await. Ini perubahan yang sering bikin bingung developer yang baru migrasi dari versi 13 atau 14.

Server Components vs Client Components

Ini konsep terpenting di Next.js modern. Secara default, semua komponen adalah Server Component. Artinya kode React dijalankan di server, bukan di browser. Kamu bisa akses database, baca file, atau panggil API internal langsung di komponen - tanpa khawatir API key bocor ke client.

Kalau komponen butuh interaktivitas - useState, useEffect, onClick, atau event browser lain - tandai dengan "use client" di baris paling atas:


"use client";

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Klik: {count}
    </button>
  );
}

Aturan praktisnya: serendah mungkin gunakan client components. Taruh interaktivitas di daun terkecil dari pohon komponen, bukan membungkus seluruh halaman dengan "use client". Halaman tetap server-rendered, hanya bagian interaktif yang dikirim JavaScript-nya ke browser.

Data Fetching: Async Component, ISR, dan Fetch di Client

Di Server Component, fetching data semudah menulis fungsi async:


export default async function Posts() {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await res.json();

  return (
    <ul>
      {posts.slice(0, 5).map((post: any) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Tanpa intervensi apa pun, halaman ini di-render di server. Hasilnya HTML lengkap - bagus untuk SEO dan first paint.

Mau halaman statis yang di-revalidate berkala? Tambahkan opsi pada fetch:


const res = await fetch("https://api.example.com/data", {
  next: { revalidate: 3600 }, // regenerasi maksimal tiap 1 jam
});

Ini yang disebut ISR (Incremental Static Regeneration). Halaman disajikan sebagai static file yang cepat, tapi Next.js diam-diam memperbaruinya di background saat data berubah. Kombinasi kecepatan static dan kesegaran dynamic.

Untuk data yang harus selalu fresh (misalnya keranjang belanja), fetch di client component dengan useEffect atau library seperti TanStack Query:


"use client";

import { useEffect, useState } from "react";

export default function LivePrice() {
  const [price, setPrice] = useState<number | null>(null);

  useEffect(() => {
    fetch("/api/price")
      .then((r) => r.json())
      .then((d) => setPrice(d.price));
  }, []);

  return <p>Harga: {price ?? "..."}</p>;
}

Route Handler: Backend Mini di Dalam Next.js

Perlu endpoint API? Buat file route.ts di dalam folder app. File app/api/price/route.ts otomatis jadi endpoint /api/price:


import { NextResponse } from "next/server";

export async function GET() {
  return NextResponse.json({ price: 15000 });
}

Route handler ini jalan di server, jadi aman untuk logic yang butuh secret seperti kredensial database atau token API. Untuk project skala kecil sampai menengah, ini bisa menggantikan backend terpisah.

SEO: Metadata API dan generateMetadata

SEO di Next.js tidak perlu plugin tambahan. Cukup export metadata dari komponen:


import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "Belajar Next.js untuk Pemula",
  description: "Tutorial Next.js dari nol: routing, server components, data fetching, sampai deploy ke VPS.",
  openGraph: {
    title: "Belajar Next.js untuk Pemula",
    description: "Tutorial Next.js dari nol sampai deploy.",
    type: "website",
  },
};

Untuk halaman dinamis, gunakan generateMetadata yang menerima params dan mengembalikan metadata berdasarkan data:


export async function generateMetadata({
  params,
}: {
  params: Promise<{ slug: string }>;
}): Promise<Metadata> {
  const { slug } = await params;
  const post = await getPost(slug);
  return {
    title: post.title,
    description: post.excerpt,
  };
}

Next.js otomatis menghasilkan tag <title>, meta description, dan Open Graph. Sitemap dan robots.txt juga bisa dibuat dari kode via app/sitemap.ts dan app/robots.ts.

Deploy ke VPS: Build, PM2, dan Nginx

Setelah aplikasi selesai, saatnya production. Build dulu:


npm run build
npm run start

Untuk production di VPS, cara paling sederhana: jalankan dengan PM2 supaya proses tetap hidup dan auto-restart kalau crash:


npm i -g pm2
pm2 start npm --name "blog-saya" -- start
pm2 save
pm2 startup

Lalu pasang nginx sebagai reverse proxy. Buat file konfigurasi di /etc/nginx/sites-available/blog-saya:


server {
    listen 80;
    server_name blog-saya.com www.blog-saya.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

Aktifkan, lalu pasang SSL dengan certbot:


sudo ln -s /etc/nginx/sites-available/blog-saya /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d blog-saya.com -d www.blog-saya.com

Website kamu sekarang live dengan HTTPS. Untuk VPS yang cocok buat project Next.js, cari provider dengan RAM minimal 1-2 GB - proses build Next.js cukup makan memori. Kalau budget terbatas, VPS 2 GB sudah sangat cukup untuk blog atau aplikasi skala kecil.

Styling: Tailwind CSS atau CSS Modules

Soal styling, Next.js tidak memaksa satu cara pun. Kalau waktu create-next-app tadi kamu pilih Tailwind CSS, kamu langsung bisa pakai utility class di komponen mana pun:


export default function Card() {
  return (
    <div className="rounded-xl bg-white p-6 shadow-md hover:shadow-lg">
      <h2 className="text-xl font-bold text-gray-900">Judul Kartu</h2>
      <p className="mt-2 text-sm text-gray-600">Deskripsi singkat di sini.</p>
    </div>
  );
}

Kelebihan Tailwind: cepat, konsisten, dan file CSS-nya otomatis dibersihkan (tree-shaking) - hanya class yang dipakai yang masuk ke production build. Kekurangannya, markup jadi panjang dan kadang susah dibaca.

Alternatifnya, CSS Modules. Setiap komponen punya file CSS sendiri yang scope-nya lokal:


import styles from "./Card.module.css";

export default function Card() {
  return (
    <div className={styles.card}>
      <h2 className={styles.title}>Judul Kartu</h2>
    </div>
  );
}

/* Card.module.css */
.card {
  border-radius: 12px;
  background: white;
  padding: 24px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.title {
  font-size: 20px;
  font-weight: 700;
}

Nama class di CSS Modules otomatis diacak (misalnya Card_card__aB3xZ), jadi tidak akan bentrok dengan halaman lain. Pilih sesuai selera: Tailwind untuk kecepatan, CSS Modules untuk struktur klasik.

Environment Variables: Simpan Secret dengan Aman

Kredensial database, API key, atau token tidak boleh ditulis langsung di kode. Next.js mendukung file .env.local di root project:


DATABASE_URL="mysql://user:password@localhost:3306/blog"
API_KEY="rahasia-jangan-bocor"
NEXT_PUBLIC_SITE_URL="https://blog-saya.com"

Variabel tanpa prefix NEXT_PUBLIC_ hanya bisa diakses di server (Server Component, Route Handler, atau fungsi di sisi server). Variabel dengan prefix NEXT_PUBLIC_ ikut ter-bundle ke JavaScript client - jadi jangan pernah taruh secret di sana. Contoh pemakaian:


// Server Component - aman
export default async function Page() {
  const dbUrl = process.env.DATABASE_URL;
  // pakai untuk koneksi database
}

// Client Component - HANYA data publik
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;

Jangan lupa tambahkan .env.local ke .gitignore supaya tidak ikut ter-commit ke repository.

Masalah Umum dan Cara Mengatasinya

Selama belajar, kamu hampir pasti nemu error-error ini. Tenang, semuanya punya solusi yang sudah terdokumentasi baik:

  • Hydration mismatch: muncul kalau HTML di server beda dengan hasil render di client. Penyebab paling sering: pakai new Date() atau Math.random() langsung di render. Solusinya, pindahkan logic ke useEffect atau gunakan suppressHydrationWarning untuk kasus tertentu.
  • params is a Promise: error "params should be awaited" muncul di Next.js 15 karena params sekarang async. Cukup tambahkan await params seperti contoh dynamic route di atas.
  • Port 3000 sudah dipakai: jalankan di port lain dengan npm run dev -- -p 3001, atau matikan proses lama dengan lsof -i :3000 lalu kill.
  • "use client" di file yang salah: directive ini hanya berlaku untuk file yang butuh interaktivitas. Kalau komponen server meng-import komponen client, pastikan batasnya jelas - jangan taruh "use client" di layout utama tanpa alasan.

Pola pikir yang membantu: baca error-nya pelan-pelan. Pesan error Next.js biasanya langsung menyebut file dan baris yang bermasalah, plus saran perbaikannya. Lebih baik dari error React biasa.

Optimasi Gambar dengan next/image

Gambar adalah penyumbang terbesar ukuran halaman web. Next.js punya komponen Image bawaan yang menangani optimasi otomatis: format WebP/AVIF, resize sesuai ukuran yang diminta, lazy loading, dan pencegahan layout shift:


import Image from "next/image";

export default function Hero() {
  return (
    <Image
      src="/images/hero.png"
      alt="Ilustrasi hero"
      width={800}
      height={400}
      priority
      className="rounded-lg"
    />
  );
}

Beberapa hal yang perlu kamu tahu:

  • width dan height wajib diisi supaya Next.js bisa menghitung rasio dan mencegah Cumulative Layout Shift (CLS). Untuk gambar yang ukurannya tidak diketahui, pakai fill dengan parent yang punya posisi relative.
  • priority dipakai untuk gambar di atas lipatan (above the fold) - gambar ini langsung dimuat tanpa lazy loading.
  • Gambar dari domain luar perlu didaftarkan di next.config.ts lewat opsi images.remotePatterns, misalnya kalau kamu pakai CDN atau storage eksternal.

Bonusnya, semua gambar otomatis di-serve dari endpoint optimasi bawaan (/_next/image), jadi kamu tidak perlu aplikasi resizer terpisah. Ini salah satu fitur yang paling saya rasakan dampaknya saat migrasi dari React + Vite.

Kesimpulan

Next.js mengubah cara saya membangun web. Yang dulu butuh 5-6 package dan konfigurasi panjang, sekarang jadi bawaan framework. Routing, SEO, SSR, API - semua beres dalam satu project. Untuk pemula yang sudah paham React dasar, Next.js adalah langkah berikutnya yang paling masuk akal di 2026.

Mulai dari project kecil: porting website portofoliomu, atau bikin blog pribadi dengan markdown. Praktik langsung jauh lebih efektif daripada nonton tutorial terus-menerus. Kalau bingung mau mulai dari mana, tiru dulu struktur project sederhana, lalu ubah pelan-pelan sesuai kebutuhanmu sendiri.

Kalau kamu baru coba Next.js, bagian mana yang paling membingungkan? Server components, atau setup awal? Tulis di kolom komentar - siapa tahu jadi artikel selanjutnya.


Bagikan artikel ini:

Komentar (0)

Belum ada komentar. Jadilah yang pertama memberikan tanggapan!

Tinggalkan Komentar