Kuasai Next.js dari dasar. Pelajari mengapa Next.js diciptakan, pahami core concepts seperti file-based routing, server components, API routes, data fetching, dan deployment. Bangun complete production-ready e-commerce platform yang cover semua Next.js fundamentals dengan best practices.

Next.js revolutionize React development dengan provide complete full-stack framework untuk building production-ready applications. Berbeda dengan traditional React SPAs, Next.js enable server-side rendering, static generation, API routes, dan seamless full-stack development. Tapi mengapa Next.js ada, dan apa yang membuatnya fundamentally different?
Dalam artikel ini, kita akan explore Next.js's philosophy, understand mengapa Next.js diciptakan, dive deep ke core concepts, dan build complete production-ready e-commerce platform yang demonstrate semua fundamental Next.js patterns.
Sebelum Next.js, React developers faced significant challenges:
Vercel created Next.js di 2016 dengan revolutionary approach:
Next.js automatically create routes berdasarkan file structure dalam app directory.
// File structure create routes automatically
app/
├── page.tsx // / (home page)
├── about/
│ └── page.tsx // /about
├── products/
│ ├── page.tsx // /products
│ └── [id]/
│ └── page.tsx // /products/:id (dynamic)
└── api/
└── products/
└── route.ts // /api/products (API route)
// Dynamic route dengan params
// app/products/[id]/page.tsx
export default function ProductPage({ params }: { params: { id: string } }) {
return <h1>Product {params.id}</h1>;
}React Server Components enable rendering pada server by default.
// Server Component (default)
// app/products/page.tsx
import { getProducts } from '@/lib/db';
export default async function ProductsPage() {
const products = await getProducts();
return (
<div>
<h1>Products</h1>
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
);
}
// Client Component (untuk interactivity)
// app/components/ProductFilter.tsx
'use client';
import { useState } from 'react';
export default function ProductFilter() {
const [filter, setFilter] = useState('');
return (
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter products..."
/>
);
}Create backend API endpoints tanpa separate server.
// app/api/products/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const products = [
{ id: 1, name: 'Product 1', price: 99.99 },
{ id: 2, name: 'Product 2', price: 149.99 },
];
return NextResponse.json(products);
}
export async function POST(request: NextRequest) {
const data = await request.json();
// Validate dan save product
const newProduct = {
id: 3,
...data,
};
return NextResponse.json(newProduct, { status: 201 });
}
// Dynamic API route
// app/api/products/[id]/route.ts
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const product = {
id: params.id,
name: 'Product',
price: 99.99,
};
return NextResponse.json(product);
}Multiple strategies untuk fetching data dalam Next.js.
// Server-side fetching (recommended)
// app/products/page.tsx
async function getProducts() {
const res = await fetch('https://api.example.com/products', {
next: { revalidate: 60 }, // ISR - revalidate every 60 seconds
});
return res.json();
}
export default async function ProductsPage() {
const products = await getProducts();
return <div>{/* render products */}</div>;
}
// Client-side fetching dengan SWR
// app/components/ProductList.tsx
'use client';
import useSWR from 'swr';
const fetcher = (url: string) => fetch(url).then(r => r.json());
export default function ProductList() {
const { data, error, isLoading } = useSWR('/api/products', fetcher);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error loading products</div>;
return (
<ul>
{data?.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
// Static generation dengan dynamic params
// app/products/[id]/page.tsx
export async function generateStaticParams() {
const products = await fetch('https://api.example.com/products').then(r => r.json());
return products.map(product => ({
id: product.id.toString(),
}));
}
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await fetch(\`https://api.example.com/products/\${params.id}\`).then(r => r.json());
return <h1>{product.name}</h1>;
}Run code sebelum requests diproses.
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
// Check authentication
const token = request.cookies.get('auth-token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
// Add custom headers
const response = NextResponse.next();
response.headers.set('X-Custom-Header', 'value');
return response;
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*'],
};Built-in image optimization untuk performance.
import Image from 'next/image';
export default function ProductImage() {
return (
<Image
src="/products/product-1.jpg"
alt="Product 1"
width={300}
height={300}
priority // Load immediately
quality={80} // Optimize quality
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
);
}Create shared layouts untuk consistent page structure.
// app/layout.tsx (root layout)
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'My App',
description: 'Generated by create next app',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<header>Navigation</header>
<main>{children}</main>
<footer>Footer</footer>
</body>
</html>
);
}
// app/dashboard/layout.tsx (nested layout)
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="dashboard">
<aside>Sidebar</aside>
<main>{children}</main>
</div>
);
}Optimize pages untuk search engines.
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Products',
description: 'Browse our products',
keywords: ['products', 'shop', 'ecommerce'],
openGraph: {
title: 'Products',
description: 'Browse our products',
url: 'https://example.com/products',
siteName: 'My Store',
images: [
{
url: 'https://example.com/og-image.jpg',
width: 1200,
height: 630,
},
],
type: 'website',
},
};
export default function ProductsPage() {
return <h1>Products</h1>;
}Handle errors gracefully dengan error boundaries.
// app/error.tsx
'use client';
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error(error);
}, [error]);
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}
// app/not-found.tsx
export default function NotFound() {
return (
<div>
<h2>Not Found</h2>
<p>Could not find requested resource</p>
</div>
);
}Manage configuration across environments.
# .env.local
DATABASE_URL=postgresql://user:password@localhost/dbname
API_KEY=your-api-key
NEXT_PUBLIC_API_URL=https://api.example.com
# .env.production
DATABASE_URL=postgresql://prod-user:prod-password@prod-host/prod-db
API_KEY=prod-api-key
NEXT_PUBLIC_API_URL=https://api.production.com// app/api/products/route.ts
export async function GET() {
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
const apiKey = process.env.API_KEY;
const res = await fetch(\`\${apiUrl}/products\`, {
headers: {
'Authorization': \`Bearer \${apiKey}\`,
},
});
return res;
}Mari build complete e-commerce platform dengan Next.js.
ecommerce-app/
├── app/
│ ├── layout.tsx
│ ├── page.tsx
│ ├── products/
│ │ ├── page.tsx
│ │ └── [id]/
│ │ └── page.tsx
│ ├── cart/
│ │ └── page.tsx
│ ├── checkout/
│ │ └── page.tsx
│ ├── api/
│ │ ├── products/
│ │ │ ├── route.ts
│ │ │ └── [id]/route.ts
│ │ ├── cart/
│ │ │ └── route.ts
│ │ └── orders/
│ │ └── route.ts
│ └── components/
│ ├── Header.tsx
│ ├── ProductCard.tsx
│ ├── Cart.tsx
│ └── Checkout.tsx
├── lib/
│ ├── db.ts
│ └── types.ts
├── middleware.ts
├── .env.local
└── next.config.jsexport interface Product {
id: string;
name: string;
description: string;
price: number;
image: string;
stock: number;
category: string;
}
export interface CartItem {
productId: string;
quantity: number;
price: number;
}
export interface Order {
id: string;
items: CartItem[];
total: number;
status: 'pending' | 'processing' | 'shipped' | 'delivered';
createdAt: Date;
}
export interface User {
id: string;
email: string;
name: string;
cart: CartItem[];
}Implementasi untuk database functions, API routes, components, dan pages sama dengan English version. Struktur dan logic tetap sama, hanya dengan Indonesian comments dan labels.
// ✅ Use Server Components by default
// ✅ Only use 'use client' ketika need interactivity
// ✅ Keep client components small dan focused
// ✅ Fetch data pada server ketika possible
// ❌ Avoid making entire pages client components
// ❌ Don't fetch data pada client jika server bisa
// ❌ Avoid unnecessary 'use client' directives// ✅ Use server-side fetching untuk SEO
// ✅ Use ISR (Incremental Static Regeneration) untuk dynamic content
// ✅ Use SWR atau React Query untuk client-side data
// ✅ Implement proper error handling
// ❌ Avoid fetching semua data pada client
// ❌ Don't ignore caching strategies
// ❌ Avoid N+1 query problems// ✅ Use Image component untuk optimization
// ✅ Implement code splitting dengan dynamic imports
// ✅ Use next/font untuk font optimization
// ✅ Monitor Core Web Vitals
// ❌ Avoid large JavaScript bundles
// ❌ Don't use unoptimized images
// ❌ Avoid render-blocking resources// ✅ Use generateMetadata untuk dynamic metadata
// ✅ Include Open Graph tags
// ✅ Use structured data (JSON-LD)
// ✅ Create sitemaps dan robots.txt
// ❌ Avoid duplicate meta tags
// ❌ Don't ignore mobile optimization
// ❌ Avoid keyword stuffing// ✅ Validate semua user input
// ✅ Use environment variables untuk secrets
// ✅ Implement CSRF protection
// ✅ Use secure headers
// ❌ Don't expose sensitive data ke client
// ❌ Avoid SQL injection vulnerabilities
// ❌ Don't skip authentication checks// ❌ Wrong - fetching dalam client component
'use client';
export default function Products() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products').then(r => r.json()).then(setProducts);
}, []);
return <div>{/* render products */}</div>;
}
// ✅ Correct - fetch pada server
export default async function Products() {
const products = await fetch('https://api.example.com/products').then(r => r.json());
return <div>{/* render products */}</div>;
}// ❌ Wrong - unoptimized image
<img src="/products/product.jpg" alt="Product" />
// ✅ Correct - optimized image
import Image from 'next/image';
<Image
src="/products/product.jpg"
alt="Product"
width={300}
height={300}
priority
/>// ❌ Wrong - no metadata
export default function ProductPage() {
return <h1>Product</h1>;
}
// ✅ Correct - include metadata
export const metadata = {
title: 'Product',
description: 'Product description',
};
export default function ProductPage() {
return <h1>Product</h1>;
}// ❌ Wrong - entire page adalah client component
'use client';
export default function Page() {
// Semua rendering happens pada client
return <div>{/* content */}</div>;
}
// ✅ Correct - hanya interactive parts adalah client components
export default function Page() {
return (
<div>
<ServerComponent />
<ClientComponent />
</div>
);
}// ❌ Wrong - no error handling
export default async function Page() {
const data = await fetch('https://api.example.com/data').then(r => r.json());
return <div>{data}</div>;
}
// ✅ Correct - handle errors
export default async function Page() {
try {
const data = await fetch('https://api.example.com/data').then(r => r.json());
return <div>{data}</div>;
} catch (error) {
return <div>Error loading data</div>;
}
}# Install Vercel CLI
npm i -g vercel
# Deploy
vercel
# Deploy ke production
vercel --prod# Build untuk production
npm run build
# Start production server
npm start
# Docker deployment
docker build -t nextjs-app .
docker run -p 3000:3000 nextjs-appNext.js revolutionize React development dengan provide complete full-stack framework untuk building production-ready applications. Dengan combine server-side rendering, static generation, API routes, dan automatic optimization, Next.js enable developers untuk build fast, scalable, dan SEO-friendly applications.
E-commerce platform yang kita build demonstrate semua core Next.js concepts dalam action. Understanding file-based routing, server components, API routes, data fetching strategies, dan deployment adalah essential untuk building modern Next.js applications.
Key takeaways:
Next steps:
Next.js adalah future of full-stack React development. Keep learning, building, dan pushing boundaries dari apa yang possible.