1. Pengenalan Next.js
Next.js adalah framework React yang dikembangkan oleh Vercel yang memungkinkan Anda membangun aplikasi web full-stack dengan mudah. Next.js menambahkan fitur-fitur penting di atas React seperti server-side rendering (SSR), static site generation (SSG), routing otomatis, API routes, dan optimasi gambar β semuanya out of the box.
Sejak diluncurkan pada tahun 2016, Next.js telah menjadi framework React paling populer untuk produksi. Perusahaan besar seperti Netflix, TikTok, Twitch, Hulu, dan Nike menggunakan Next.js untuk membangun website mereka.
Mengapa Memilih Next.js?
| Fitur | Penjelasan |
|---|---|
| Server-Side Rendering | Halaman di-render di server, menghasilkan HTML yang langsung dikirim ke browser β lebih cepat dan SEO-friendly |
| Static Site Generation | Halaman bisa di-build saat build time untuk performa maksimal |
| File-Based Routing | Cukup buat file di folder, routing otomatis dibuat β tidak perlu konfigurasi manual |
| API Routes | Buat API backend langsung di dalam proyek Next.js tanpa server terpisah |
| Optimasi Otomatis | Image optimization, font optimization, script optimization β semua built-in |
| Full-Stack | Frontend dan backend dalam satu proyek yang sama |
Next.js vs React vs Framework Lain
| Aspek | Next.js | React (SPA) | Remix |
|---|---|---|---|
| Rendering | SSR, SSG, ISR, CSR | CSR saja | SSR |
| Routing | File-based (App Router) | React Router (manual) | File-based |
| API | API Routes / Route Handlers | Perlu backend terpisah | Loader/Action |
| SEO | π’ Sangat baik | π‘ Perlu SSR setup | π’ Sangat baik |
| Belajar | π‘ Sedang | π’ Mudah | π‘ Sedang |
| Deployment | Vercel, Docker, Self-host | Static hosting | Various |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β EKOSISTEM REACT / WEB MODERN β β β β βββββββββββββββββββββββββββββββββββββββββββββββββββ β β β Next.js β β β β βββββββββββββ ββββββββββββ βββββββββββββββββ β β β β βApp Router β β API Routeβ βServer Componentβ β β β β βββββββββββββ ββββββββββββ βββββββββββββββββ β β β β βββββββββββββ ββββββββββββ βββββββββββββββββ β β β β β SSR/SSG β βImage Opt β β Middleware β β β β β βββββββββββββ ββββββββββββ βββββββββββββββββ β β β βββββββββββββββββββββββββββββββββββββββββββββββββββ β β β² β β β built on β β βββββββββββ΄βββββββββββ β β β React.js β β β β (Library UI) β β β ββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Setup Proyek Next.js
Untuk membuat proyek Next.js baru, jalankan perintah berikut di terminal:
# Buat proyek Next.js baru dengan App Router npx create-next-app@latest belajar-nextjs # Pilihan yang disarankan saat setup: # β Would you like to use TypeScript? β Yes # β Would you like to use ESLint? β Yes # β Would you like to use Tailwind CSS? β Yes # β Would you like to use `src/` directory? β Yes # β Would you like to use App Router? β Yes # β Would you like to customize the default import alias? β No # Masuk ke direktori proyek cd belajar-nextjs # Jalankan development server npm run dev # Output: # β² Next.js 15.x # - Local: http://localhost:3000 # - Environments: .env.local
Struktur Proyek Next.js
belajar-nextjs/ βββ node_modules/ βββ public/ β File statis (gambar, favicon) β βββ next.svg β βββ vercel.svg βββ src/ β βββ app/ β App Router (utama) β βββ layout.tsx β Root layout (wajib) β βββ page.tsx β Halaman utama (/) β βββ globals.css β CSS global β βββ about/ β β βββ page.tsx β Halaman /about β βββ blog/ β β βββ page.tsx β Halaman /blog β β βββ [slug]/ β β βββ page.tsx β Halaman /blog/[slug] β βββ api/ β βββ hello/ β βββ route.ts β API endpoint /api/hello βββ .env.local β Environment variables βββ next.config.js β Konfigurasi Next.js βββ package.json βββ tsconfig.json β Konfigurasi TypeScript
Next.js versi terbaru menggunakan App Router secara default (folder app/). Versi lama menggunakan Pages Router (folder pages/). Untuk proyek baru, gunakan App Router karena mendukung fitur-fitur terbaru seperti React Server Components dan streaming.
2. SSR vs CSR: Memahami Rendering
Salah satu alasan utama menggunakan Next.js adalah dukungan berbagai strategi rendering. Memahami perbedaan antara Client-Side Rendering (CSR) dan Server-Side Rendering (SSR) sangat penting untuk membangun aplikasi web yang cepat dan SEO-friendly.
Client-Side Rendering (CSR)
Pada CSR, seluruh aplikasi di-render di browser menggunakan JavaScript. Browser pertama-tama menerima HTML kosong, lalu JavaScript dijalankan untuk mengisi konten. Ini adalah cara kerja default React SPA (Single Page Application).
# Alur Client-Side Rendering (CSR): # # 1. User mengakses halaman # 2. Server mengirim HTML kosong + bundle JS besar # 3. Browser mengunduh JS (bisa 1-3 detik) # 4. JavaScript dijalankan β render konten # 5. User baru bisa melihat dan berinteraksi # # β Masalah: # - Loading lambat (blank screen sampai JS selesai) # - SEO buruk (search engine melihat HTML kosong) # - First Contentful Paint (FCP) lama # Contoh: React SPA biasa dengan React Router # β Semua rendering terjadi di browser # β Data di-fetch dari client setelah halaman dimuat
Server-Side Rendering (SSR)
Pada SSR, halaman di-render di server setiap kali ada request. Server mengirim HTML lengkap yang langsung bisa ditampilkan browser β tanpa menunggu JavaScript.
# Alur Server-Side Rendering (SSR):
#
# 1. User mengakses halaman
# 2. Server meng-fetch data dan render komponen
# 3. Server mengirim HTML lengkap ke browser
# 4. Browser langsung menampilkan konten
# 5. JavaScript "hydration" β interaktif
#
# β
Keuntungan:
# - Konten langsung terlihat (FCP cepat)
# - SEO sangat baik (search engine dapat HTML lengkap)
# - Cocok untuk konten dinamis yang sering berubah
# Di Next.js, gunakan fungsi ini untuk SSR:
# export async function generateMetadata() { ... }
# fetch() dengan cache: 'no-store'
Perbandingan Rendering di Next.js
| Metode | Kapan Render | Konten | Cocok Untuk |
|---|---|---|---|
| SSR (Dynamic) | Setiap request | Terbaru | Dashboard, halaman user |
| SSG (Static) | Saat build | Statis | Blog, landing page |
| ISR | Saat build + revalidate | Semi-dinamis | E-commerce, berita |
| CSR | Di browser | Dinamis | App interaktif |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β CLIENT-SIDE (CSR) β β β β Browser Server β β ββββββββ ββββββββ β β β ββββββββΆβ β Request β β β βββββββββ β HTML kosong + JS bundle β β β β ββββββββ β β β β³ β Download JS... (lambat) β β β β³ β Execute JS... β β β β β Konten muncul (terlambat) β β ββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β SERVER-SIDE (SSR) β β β β Browser Server β β ββββββββ ββββββββ β β β ββββββββΆβ β Request β β β β β β‘ β Render di server β β β β β β‘ β Fetch data β β β βββββββββ β HTML lengkap β β β β β ββββββββ Konten langsung terlihat! β β ββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Contoh SSR di Next.js App Router
// src/app/blog/page.tsx
// Secara default, komponen di App Router adalah Server Component
// Jadi fetch() di sini berjalan di server
interface Post {
id: number;
title: string;
body: string;
}
// Komponen ini di-render di server (SSR)
export default async function BlogPage() {
// Data di-fetch di server β tidak dikirim ke browser
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
cache: 'no-store', // SSR: selalu data terbaru
});
const posts: Post[] = await response.json();
return (
<main>
<h1>Daftar Blog</h1>
<p>Total: {posts.length} artikel</p>
{posts.slice(0, 10).map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.body}</p>
</article>
))}
</main>
);
}
Static Site Generation (SSG) dengan ISR
// src/app/products/page.tsx
// Static generation dengan revalidation setiap 60 detik
export default async function ProductsPage() {
// next: { revalidate: 60 } = ISR
// Halaman di-build secara statis, tapi di-regenerate
// setiap 60 detik jika ada request baru
const response = await fetch('https://api.example.com/products', {
next: { revalidate: 60 },
});
const products = await response.json();
return (
<div>
<h1>Produk Kami</h1>
<div className="product-grid">
{products.map((product: any) => (
<div key={product.id} className="product-card">
<h3>{product.name}</h3>
<p>Rp {product.price.toLocaleString('id-ID')}</p>
</div>
))}
</div>
</div>
);
}
3. App Router dan Struktur Proyek
App Router adalah sistem routing baru di Next.js 13+ yang menggunakan folder dan file khusus untuk mendefinisikan rute. Ini menggantikan Pages Router yang lebih lama dan mendukung React Server Components, streaming, dan fitur modern lainnya.
Konvensi File dalam App Router
| File | Fungsi |
|---|---|
page.tsx | Membuat rute yang bisa diakses user |
layout.tsx | Layout bersama untuk rute dan child-nya |
loading.tsx | UI loading (suspense boundary) |
error.tsx | Error boundary untuk menangkap error |
not-found.tsx | Halaman 404 untuk rute tersebut |
route.ts | API endpoint (Route Handler) |
template.tsx | Seperti layout, tapi re-mount setiap navigasi |
Membuat Rute Dasar
# File-based routing di App Router: # # src/app/page.tsx β / # src/app/about/page.tsx β /about # src/app/blog/page.tsx β /blog # src/app/blog/[slug]/page.tsx β /blog/apa-saja # src/app/dashboard/ # βββ layout.tsx β Layout bersama # βββ page.tsx β /dashboard # βββ settings/page.tsx β /dashboard/settings # βββ profile/page.tsx β /dashboard/profile # # Tidak perlu konfigurasi router! # Cukup buat folder + file page.tsx
Contoh Root Layout
// src/app/layout.tsx (file wajib di App Router)
// Ini membungkus semua halaman dalam aplikasi
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'Aplikasi Next.js Saya',
description: 'Belajar Next.js dengan mudah',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="id">
<body>
<header>
<nav>
<a href="/">Beranda</a>
<a href="/about">Tentang</a>
<a href="/blog">Blog</a>
</nav>
</header>
<main>{children}</main>
<footer>
<p>© 2026 Aplikasi Saya</p>
</footer>
</body>
</html>
);
}
Dynamic Routes
// src/app/blog/[slug]/page.tsx
// Route dinamis: /blog/nextjs-dasar, /blog/react-hooks, dll.
interface BlogPostProps {
params: Promise<{ slug: string }>;
}
export default async function BlogPost({ params }: BlogPostProps) {
const { slug } = await params;
// Fetch data berdasarkan slug
const response = await fetch(`https://api.example.com/posts/${slug}`);
const post = await response.json();
return (
<article>
<h1>{post.title}</h1>
<time>{post.date}</time>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
// Generate metadata dinamis untuk SEO
export async function generateMetadata({ params }: BlogPostProps) {
const { slug } = await params;
const response = await fetch(`https://api.example.com/posts/${slug}`);
const post = await response.json();
return {
title: post.title,
description: post.excerpt,
};
}
Layout mempertahankan state saat navigasi antar child routes β tidak re-mount. Sedangkan Template re-mount setiap kali user berpindah rute. Gunakan layout untuk sidebar/navigasi yang konsisten, dan template untuk animasi transisi halaman atau efek yang perlu reset state.
4. Server Components vs Client Components
Di Next.js App Router, setiap komponen secara default adalah Server Component β artinya komponen tersebut di-render di server dan tidak mengirim JavaScript ke browser. Ini menghasilkan bundle size yang lebih kecil dan performa yang lebih baik.
Namun, jika komponen membutuhkan interaksi pengguna (click, input, dll), browser API, atau React hooks, komponen tersebut harus ditandai sebagai Client Component menggunakan direktif "use client".
Perbandingan Server vs Client Components
| Fitur | Server Component | Client Component |
|---|---|---|
| Rendering | Di server | Di browser (dengan hydration) |
| JavaScript ke client | β Tidak | β Ya |
| useState/useEffect | β Tidak bisa | β Bisa |
| Akses database/API | β Langsung | β Perlu fetch() |
| Event handler | β Tidak bisa | β Bisa |
| Browser API | β Tidak bisa | β Bisa |
| Bundle size | π’ 0 KB (tidak dikirim) | π‘ Sesuai kode |
Server Component (Default)
// app/posts/page.tsx β Server Component (default, tanpa "use client")
// Bisa langsung akses database, file system, API rahasia
import { db } from '@/lib/database';
export default async function PostsPage() {
// Langsung query database! API key tetap aman di server
const posts = await db.query('SELECT * FROM posts ORDER BY created_at DESC');
return (
<div>
<h1>Daftar Postingan</h1>
{posts.map((post: any) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
<time>{new Date(post.created_at).toLocaleDateString('id-ID')}</time>
</article>
))}
</div>
);
}
// Komponen ini TIDAK mengirim JavaScript ke browser
// Semua kode di atas hanya berjalan di server
// Database credentials TIDAK pernah sampai ke browser
Client Component
'use client'; // β Directive ini WAJIB di baris pertama
import { useState, useEffect } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) return null; // Hindari hydration mismatch
return (
<div className="counter">
<h2>Hitungan: {count}</h2>
<button onClick={() => setCount(c => c + 1)}>
Tambah (+1)
</button>
<button onClick={() => setCount(c => c - 1)}>
Kurang (-1)
</button>
<button onClick={() => setCount(0)}>
Reset
</button>
</div>
);
}
Pola Terbaik: Komposisi Server dan Client
// components/AddToCartButton.tsx β Client Component
'use client';
import { useState } from 'react';
export default function AddToCartButton({ productId }: { productId: string }) {
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
await fetch('/api/cart', {
method: 'POST',
body: JSON.stringify({ productId }),
});
setLoading(false);
alert('Produk ditambahkan ke keranjang!');
};
return (
<button onClick={handleClick} disabled={loading}>
{loading ? 'Menambahkan...' : 'π Tambah ke Keranjang'}
</button>
);
}
// app/products/[id]/page.tsx β Server Component
import AddToCartButton from '@/components/AddToCartButton';
import { db } from '@/lib/database';
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const product = await db.query('SELECT * FROM products WHERE id = ?', [id]);
return (
<div className="product-detail">
<h1>{product.name}</h1>
<p>{product.description}</p>
<p className="price">Rp {product.price.toLocaleString('id-ID')}</p>
{/* Client Component di-render di dalam Server Component */}
<AddToCartButton productId={id} />
</div>
);
}
Prinsipnya: Server Component di luar, Client Component di dalam. Server Component bisa mengimpor dan merender Client Component sebagai children. Tapi Client Component TIDAK BISA mengimpor Server Component. Jika butuh pola itu, render Server Component sebagai children dari Client Component.
5. API Routes: Backend dalam Next.js
Next.js memungkinkan Anda membuat API endpoint langsung di dalam proyek yang sama menggunakan Route Handlers. Ini sangat berguna untuk form submission, autentikasi, webhook, atau proxy API β tanpa perlu server backend terpisah.
Membuat API Route Dasar
// src/app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
// GET /api/users
export async function GET(request: NextRequest) {
// Ambil query parameters
const searchParams = request.nextUrl.searchParams;
const page = parseInt(searchParams.get('page') || '1');
const limit = parseInt(searchParams.get('limit') || '10');
// Di sini Anda bisa fetch dari database
const users = [
{ id: 1, nama: 'Budi Santoso', email: 'budi@email.com' },
{ id: 2, nama: 'Sari Dewi', email: 'sari@email.com' },
{ id: 3, nama: 'Andi Pratama', email: 'andi@email.com' },
];
return NextResponse.json({
success: true,
data: users,
pagination: { page, limit, total: users.length },
});
}
POST, PUT, DELETE Methods
// src/app/api/users/route.ts (tambahkan method lain)
// POST /api/users β Buat user baru
export async function POST(request: NextRequest) {
try {
const body = await request.json();
// Validasi input
if (!body.nama || !body.email) {
return NextResponse.json(
{ error: 'Nama dan email wajib diisi' },
{ status: 400 }
);
}
// Simpan ke database (contoh)
const newUser = {
id: Date.now(),
nama: body.nama,
email: body.email,
};
// await db.insert('users', newUser);
return NextResponse.json(
{ success: true, data: newUser },
{ status: 201 }
);
} catch (error) {
return NextResponse.json(
{ error: 'Gagal membuat user' },
{ status: 500 }
);
}
}
// src/app/api/users/[id]/route.ts
// DELETE /api/users/:id β Hapus user
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
// await db.delete('users', id);
return NextResponse.json({ success: true, message: `User ${id} dihapus` });
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β NEXT.JS APPLICATION β β β β βββββββββββββββββββββββββ βββββββββββββββββββββββ β β β FRONTEND β β BACKEND β β β β β β β β β β βββββββββββββββββββ β β βββββββββββββββββ β β β β β Server Componentβ β β β API Routes β β β β β β (SSR/SSG) β β β β /api/users β β β β β βββββββββββββββββββ β β β /api/auth β β β β β βββββββββββββββββββ β β β /api/products β β β β β β Client Componentβ β β βββββββββββββββββ β β β β β (Interaktif) β β β β β β β β βββββββββββββββββββ β β βΌ β β β βββββββββββββββββββββββββ β βββββββββββββββββ β β β β β β Database β β β β βΌ β β PostgreSQL β β β β βββββββββββββββββββββββ β β MongoDB β β β β β Browser β β βββββββββββββββββ β β β β (User melihat UI) β β β β β βββββββββββββββββββββββ βββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
6. Data Fetching di Next.js
Next.js memperluas API fetch() bawaan browser untuk memberikan kontrol penuh atas caching dan revalidasi data. Anda bisa melakukan fetching data langsung di Server Components, yang memungkinkan akses database atau API secara aman.
Fetching di Server Component
// 1. Fetch dengan caching (default) β data di-cache
async function getProducts() {
const res = await fetch('https://api.example.com/products');
return res.json();
}
// 2. Fetch tanpa caching (SSR) β data selalu terbaru
async function getDashboard() {
const res = await fetch('https://api.example.com/dashboard', {
cache: 'no-store', // Setiap request: data baru dari server
});
return res.json();
}
// 3. Fetch dengan ISR (revalidate setiap 60 detik)
async function getArticles() {
const res = await fetch('https://api.example.com/articles', {
next: { revalidate: 60 }, // Cache 60 detik, lalu regenerate
});
return res.json();
}
// 4. Fetch dengan tag (untuk on-demand revalidation)
async function getPost(slug: string) {
const res = await fetch(`https://api.example.com/posts/${slug}`, {
next: { tags: ['posts', `post-${slug}`] },
});
return res.json();
}
// Penggunaan di komponen
export default async function HomePage() {
const products = await getProducts();
const articles = await getArticles();
return (
<div>
<h1>Beranda</h1>
<section>
<h2>Produk Terbaru</h2>
{products.map((p: any) => (
<div key={p.id}>{p.name} β Rp {p.price}</div>
))}
</section>
<section>
<h2>Artikel Terbaru</h2>
{articles.map((a: any) => (
<article key={a.id}>{a.title}</article>
))}
</section>
</div>
);
}
Fetching di Client Component
'use client';
import { useState, useEffect } from 'react';
interface Product {
id: number;
name: string;
price: number;
}
export default function ProductSearch() {
const [query, setQuery] = useState('');
const [products, setProducts] = useState<Product[]>([]);
const [loading, setLoading] = useState(false);
// Fetch data saat query berubah
useEffect(() => {
if (!query.trim()) {
setProducts([]);
return;
}
const controller = new AbortController();
async function searchProducts() {
setLoading(true);
try {
const res = await fetch(`/api/products?q=${query}`, {
signal: controller.signal,
});
const data = await res.json();
setProducts(data);
} catch (err: any) {
if (err.name !== 'AbortError') {
console.error('Gagal mencari produk:', err);
}
} finally {
setLoading(false);
}
}
// Debounce: tunggu 300ms setelah user berhenti mengetik
const timer = setTimeout(searchProducts, 300);
return () => {
clearTimeout(timer);
controller.abort();
};
}, [query]);
return (
<div className="search">
<input
type="text"
placeholder="Cari produk..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
{loading && <p>Mencari...</p>}
<ul>
{products.map((product) => (
<li key={product.id}>
{product.name} β Rp {product.price.toLocaleString('id-ID')}
</li>
))}
</ul>
</div>
);
}
Jika Anda memiliki beberapa data yang saling tidak bergantung, jangan fetch secara berurutan (sequential). Gunakan Promise.all() untuk fetch secara paralel. Next.js juga bisa melakukan deduplication β jika URL yang sama di-fetch beberapa kali, hanya satu request yang dikirim.
7. Deployment ke Vercel
Vercel adalah platform hosting yang dikembangkan oleh pembuat Next.js. Deployment ke Vercel sangat mudah β cukup push ke GitHub dan Vercel otomatis build dan deploy aplikasi Anda.
Cara Deploy ke Vercel
# Cara 1: Deploy via Vercel CLI # Instal Vercel CLI npm i -g vercel # Login ke Vercel vercel login # Deploy proyek (jalankan di root proyek) vercel # Deploy ke production vercel --prod # Cara 2: Deploy via GitHub (Disarankan) # 1. Push kode ke repository GitHub git init git add . git commit -m "Initial commit" git remote add origin https://github.com/username/nextjs-app.git git push -u origin main # 2. Buka vercel.com β Import Project # 3. Pilih repository GitHub Anda # 4. Vercel otomatis detect Next.js dan deploy # 5. Setiap push ke main = auto deploy!
Environment Variables
# .env.local (jangan commit ke git!) DATABASE_URL=postgresql://user:pass@localhost:5432/mydb NEXT_PUBLIC_API_URL=https://api.example.com SECRET_KEY=rahasia-jangan-dibagikan # Variabel dengan prefix NEXT_PUBLIC_ bisa diakses di browser # Variabel TANPA prefix hanya bisa diakses di server # Ini menjaga API key dan secret tetap aman # Di Vercel Dashboard: # Project Settings β Environment Variables # Tambahkan variabel yang sama untuk Production, Preview, Development
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β ALUR DEPLOY NEXT.JS KE VERCEL β β β β βββββββββββ ββββββββββββ βββββββββββββββββ β β β Local β β GitHub β β Vercel β β β β Dev ββββββΆβ Push ββββββΆβ Auto Build β β β β β β β β Auto Deploy β β β βββββββββββ ββββββββββββ βββββββββ¬ββββββββ β β β β β ββββββββΌβββββββ β β β Production β β β β yourapp. β β β β vercel.app β β β βββββββββββββββ β β β β Fitur Vercel: β β β Auto SSL/HTTPS β β β Global CDN β β β Preview Deployment (per PR) β β β Serverless Functions (API Routes) β β β Edge Functions (Middleware) β β β Analytics β β β Environment Variables β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Selain Vercel, Next.js juga bisa di-deploy ke platform lain. Gunakan output: 'standalone' di next.config.js untuk menghasilkan build yang bisa dijalankan dengan Docker. Anda juga bisa deploy ke Netlify, AWS, Railway, atau VPS biasa.
8. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Next.js: