π Layouting Gak Cuma layout.tsx, Lho!
Bayangin kamu lagi ngerjain proyek Next.js. Kamu udah nerapin semua best practice: pakai app router, file layout.tsx udah rapi, semua halaman ada page.tsx-nya, dan UI-nya lumayan kece lah. Tapi suatu hari, temenmu buka halaman produk yang gak ada di database, eh malah ketemu halaman putih polos. Nggak ada tulisan "404" kek, apalagi yang lucu-lucu. Dia bilang, "Ini beneran error atau sinyal WiFi-ku yang ngaco?"
Nah loh π
Atau pernah juga kamu pengen bikin animasi setiap kali navigasi ke halaman baru, tapi ternyata layout.tsx nggak ngerender ulang... karena ya emang dia cuma dirender sekali pas awal aja.
Di sinilah banyak developer mulai sadar: ternyata ada file layouting lain yang jarang dibahas, tapi super powerful β template.tsx, error.tsx, dan not-found.tsx.
File-file ini bukan cuma "tempelan tambahan", tapi solusi praktis buat banyak masalah UX/UI di aplikasi kamu. Misalnya:
- Navigasi antar halaman tetap smooth tapi punya efek animasi? Gunakan
template.tsx. - Mau halaman 404 yang kontekstual per folder? Tinggal tambahin
not-found.tsx. - Mau error yang gak bikin user panik?
error.tsxsiap menyelamatkan hari.
Contohnya nih:
// app/blog/[slug]/not-found.tsx
export default function NotFound() {
return (
<div className="text-center py-10">
<h1 className="text-3xl font-bold">Blognya gak ketemu, cuy π’</h1>
<p>Kamu yakin link-nya bener?</p>
</div>
);
}
Dengan file itu aja, tampilan 404 kamu langsung lebih manusiawi daripada "This page could not be found" default-nya Next.js.
Jadi, kalau kamu selama ini cuma ngandelin layout.tsx dan page.tsx, it's time to upgrade your layouting game. Yuk, kita bahas satu per satu fitur keren ini yang sering terlupakan, tapi sebenernya sangat berguna! π‘
π§© Sekilas tentang App Router dan Layouting di Next.js 15
Oke, sebelum kita masuk lebih dalam ke template.tsx, error.tsx, dan not-found.tsx, ada baiknya kita ngopi dulu sebentar sambil ngulang konsep dasar App Routerβbiar gak bingung di tengah jalan.
π Folder-Based Routing: Bikin Halaman = Bikin Folder
Di Next.js 15 (dan sejak versi 13, sebenarnya), kita udah dikenalin dengan App Router, sistem routing baru yang berbasis folder. Gak perlu lagi bikin file pages/index.js, sekarang kamu cukup buat folder app dan isi dengan struktur seperti ini:
app/
ββ layout.tsx
ββ page.tsx
ββ about/
ββ layout.tsx
ββ page.tsx
Setiap folder di bawah app/ bisa punya file page.tsx, yang akan dirender sebagai halaman, dan layout.tsx, yang berfungsi sebagai wrapper untuk semua halaman anaknya. Jadi semacam template tetap yang ngebungkus children.
Misalnya:
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<header>π§ Navigasi</header>
<main>{children}</main>
<footer>Β© 2025 JabirDev</footer>
</body>
</html>
);
}
ποΈ Hirarki Layouting: Siapa Render Duluan?
Dalam App Router, setiap folder bisa punya layout sendiri. Ini artinya layout bisa bersarang dan saling mewarisi. Urutannya kira-kira begini:
layout.tsxβ Ngebungkus semua children di foldernya.template.tsxβ Miriplayout, tapi dirender ulang tiap navigasi (lebih lanjut nanti ya).page.tsxβ Komponen utama halaman.error.tsxβ Fallback kalau terjadi error saat render.not-found.tsxβ Fallback kalau halaman gak ditemukan atau kamu panggilnotFound().
Gambarnya kira-kira kayak gini:
app/
ββ layout.tsx β Global layout (paling luar)
ββ page.tsx β Halaman utama
ββ error.tsx β Error handler
ββ not-found.tsx β 404 handler
ββ dashboard/
ββ layout.tsx β Nested layout
ββ template.tsx β Dynamic layout
ββ page.tsx β Halaman dashboard
Jadi, setiap kali kamu masuk ke /dashboard, yang ke-render adalah:
app/layout.tsxβ global layoutapp/dashboard/layout.tsxβ layout spesifik dashboardapp/dashboard/template.tsxβ jika ada, dirender ulang tiap navigasiapp/dashboard/page.tsxβ konten halaman dashboard
Dengan sistem ini, kamu bisa bikin layout yang benar-benar modular dan reusable. Tapi tentu aja, makin fleksibel, makin banyak yang bisa kelewat. Makanya, yuk lanjut ke fitur-fitur layouting yang sering terlupakan tapi penting banget: template.tsx, error.tsx, dan not-found.tsx.
π§ͺ template.tsx: Layout yang Dinamis di Setiap Navigasi
Kalau layout.tsx itu ibarat kerangka tetap yang gak berubah selama user menjelajah aplikasi, maka template.tsx adalah layout dinamis yang bakal dirender ulang setiap kali user navigasi ke halaman baru dalam satu segmen.
Jadi, buat kamu yang suka efek-efek dramatis saat pindah halaman, atau mau bikin layout yang punya "perasaan berbeda" tiap klik link, template.tsx adalah sahabat baru kamu.
π€ Fungsi dan Kapan Pakainya?
template.tsx berfungsi hampir sama seperti layout.tsx, yaitu sebagai pembungkus halaman. Tapi bedanya, template.tsx tidak di-cache dan selalu dirender ulang saat navigasi, bahkan kalau halaman yang dikunjungi masih dalam segmen yang sama.
Ini cocok banget dipakai saat kamu ingin:
- Menampilkan animasi transisi antar halaman.
- Memberikan pengalaman random, misalnya mengganti background berbeda tiap navigasi.
- Melakukan reset local state antar halaman.
- Menambahkan logika efek visual tertentu yang tidak boleh persist.
π§ͺ Contoh Implementasi Sederhana
Misalnya kamu pengen setiap halaman dalam folder /product punya animasi fade-in tiap kali dibuka:
// src/app/product/template.tsx
export default function Template({ children }: { children: React.ReactNode }) {
return (
<div className="animate-fade-in min-h-screen">
{children}
</div>
);
}
Kalau kamu pakai Tailwind CSS, kamu bisa tambahkan animasinya di konfigurasi:
@theme inline {
...
--animate-fade-in: fadein 1s ease-in;
@keyframes fadein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
}
Setiap user klik halaman /product?kategori=smartphones ke /projects/123, maka seluruh layout akan fade-in ulang. Keren, kan?

βοΈ Perbandingan layout.tsx vs template.tsx
- Persist saat navigasi:
layout.tsxβ Ya, tetap dipertahankan selama navigasi.template.tsxβ Tidak, akan dirender ulang setiap kali navigasi.
- Frekuensi render:
layout.tsxβ Dirender sekali saat pertama kali masuk segmen.template.tsxβ Dirender ulang tiap kali navigasi dalam segmen.
- Kegunaan utama:
layout.tsx: Cocok untuk struktur global seperti header, sidebar, dan footer yang tetap.template.tsx: Cocok untuk efek transisi halaman, animasi masuk-keluar, atau reset state lokal.
- State persist:
layout.tsxβ State lokal tetap terjaga.template.tsxβ State akan hilang karena dirender ulang.
π‘ Kesimpulan Mini
Jadi, kalau layout.tsx itu semacam rumah yang selalu sama bentuknya tiap kamu masuk, template.tsx itu lebih seperti panggung teater β tiap adegan (halaman) bisa punya suasana dan gaya yang berbeda.
Gunakan template.tsx saat kamu ingin kasih sentuhan personal dan dinamis pada pengalaman pengguna. Tapi hati-hati juga: karena dia dirender ulang terus, jangan simpan state yang pengen dipertahankan di dalamnya.
β not-found.tsx: Buat 404 yang Lebih Kontekstual
Kamu pasti udah nggak asing sama halaman 404 β halaman yang muncul kalau user nyasar ke alamat yang gak ada. Tapi di Next.js 15 (dengan App Router), not-found.tsx bukan cuma sekadar halaman darurat. Sekarang kamu bisa kustomisasi 404 berdasarkan folder atau segmen, jadi lebih kontekstual dan personal.
π§ Fungsi Utama
not-found.tsx akan otomatis dirender ketika:
- User mengakses halaman yang tidak tersedia dalam folder tersebut.
- Kamu secara eksplisit memanggil
notFound()dari server component atau server action.
Misalnya:
import { getProduct } from "@/actions/product/getProduct";
import { notFound } from "next/navigation";
import Image from "next/image";
interface DetailPageProps {
params: Promise<{
id: string;
}>;
}
export default async function DetailPage({ params }: DetailPageProps) {
const { id } = await params;
const product = await getProduct(id);
if (!product) return notFound();
return (
<div className="max-w-4xl mx-auto px-4 py-8">
<div className="flex flex-col md:flex-row gap-8">
<Image
src={product.thumbnail}
alt={product.title}
width={400}
height={400}
className="object-cover rounded-md shadow-md"
/>
<div className="flex-1 space-y-4">
<h1 className="text-3xl font-bold">{product.title}</h1>
<p className="text-gray-600">{product.description}</p>
<div className="text-xl font-semibold text-green-600">
${product.price}{" "}
<span className="text-sm text-gray-500 line-through ml-2">
$
{Math.round(
product.price / (1 - product.discountPercentage / 100)
)}
</span>
<span className="ml-2 text-sm text-red-500">
-{product.discountPercentage}%
</span>
</div>
<div className="flex items-center gap-2">
<span className="text-yellow-500">β {product.rating}</span>
<span className="text-sm text-gray-500">
({product.stock} stock)
</span>
</div>
<div className="text-sm text-gray-500">
<p>
<strong>Brand:</strong> {product.brand}
</p>
<p>
<strong>Category:</strong> {product.category}
</p>
<p>
<strong>SKU:</strong> {product.sku}
</p>
<p>
<strong>Weight:</strong> {product.weight} g
</p>
<p>
<strong>Dimensions:</strong> {product.dimensions.width}Γ
{product.dimensions.height}Γ{product.dimensions.depth} cm
</p>
</div>
<div>
<strong className="text-sm">Tags:</strong>
<div className="flex flex-wrap gap-2 mt-1">
{product.tags.map((tag) => (
<span
key={tag}
className="px-2 py-1 bg-gray-200 text-sm rounded-md"
>
#{tag}
</span>
))}
</div>
</div>
</div>
</div>
</div>
);
}
Kalau getProduct gak nemu data, maka langsung dilempar ke file not-found.tsx di folder product.
ποΈ Bisa Dibikin Per Folder
Inilah keren dan fleksibelnya App Router: kamu bisa bikin file not-found.tsx di folder mana pun! Jadi, tampilan 404 untuk /blog bisa beda dari 404 di /products.
Contoh implementasi:
// app/products/not-found.tsx
export default function NotFound() {
return (
<div className="text-center py-20">
<h1 className="text-4xl font-bold text-red-600">Produk tidak ditemukan</h1>
<p className="mt-4 text-gray-500">Coba cari produk lainnya ya π</p>
</div>
);
}
Dengan begini, pengalaman pengguna jadi lebih relevan dan terasa "dirancang", bukan cuma error generik.

β Best Practice
- Selalu siapkan
not-found.tsxuntuk segmen dengan URL dinamis ([slug],[id], dsb). - Gunakan tone yang sesuai dengan konteks folder. Misalnya:
/blog: bisa santai atau humoris./dashboard: bisa lebih formal dan informatif.
- Tetap pastikan secara desain konsisten dengan layout aplikasi secara keseluruhan.
Next.js kasih kita tools untuk jadi lebih peduli terhadap UXβeven ketika user salah ketik link. Dan not-found.tsx adalah senjata utama kita buat itu. Lanjut yuk ke error.tsx, si penyelamat saat aplikasi tiba-tiba error di runtime! π₯
π₯ error.tsx: Tangkap dan Tampilkan Error dengan Gaya
Pernah gak sih kamu buka halaman di web, terus tiba-tiba layar putih doang, atau muncul error merah kayak kiamat kecil di browser? π΅ Nah, Next.js 15 ngasih solusi elegan buat itu lewat error.tsx.
π¨ Fungsi dan Kelebihannya
error.tsx adalah file khusus yang bisa kamu taruh di folder mana pun di App Router untuk menangkap dan menampilkan error runtime secara custom dan stylish. Gak perlu lagi panik atau tampilkan error yang bikin user bingung. Sekarang kamu bisa bilang ke user, βTenang, error ini udah ditangani kok. Coba lagi, ya!β
Yang penting: error.tsx hanya menangkap error yang terjadi saat render, fetch, atau dalam server component.
ποΈ Bisa Dibikin Per Segmen
Kamu bisa punya error.tsx di app/, di app/dashboard/, bahkan di app/products/[id]/. Tiap segmen bisa punya error handler sendiri yang sesuai konteks. Jadi, kamu bisa kasih pesan berbeda untuk error di dashboard admin dan error di halaman produk user.
βοΈ Wajib Pakai 'use client'
Karena komponen ini butuh akses ke useEffect dan fungsi reset(), maka harus ditandai sebagai client component. Tanpa ini, error handler kamu gak bakal jalan.
π§ͺ Contoh Implementasi
'use client';
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
useEffect(() => {
// Kirim ke log monitoring kalau perlu
console.error('Terjadi error:', error);
}, [error]);
return (
<div className="p-10 text-center">
<h2 className="text-2xl font-semibold text-red-600">Ups! Ada error nih π§¨</h2>
<p className="mt-4 text-gray-500">{error.message}</p>
<button
onClick={reset}
className="mt-6 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Coba Lagi
</button>
</div>
);
}
Kamu bisa tambahkan animasi, ilustrasi SVG, atau bahkan redirect kalau mau. Tapi yang penting: kasih user jalan keluar dari error itu. reset() akan me-retry render komponen yang error tadi.
Untuk keperluan demo kita akan buat halaman produk jadi error dengan cara tambahkan kode ini:
export default async function ProductPage({ searchParams }: ProductPageProps) {
// Kode untuk test error
throw new Error("Simulasi error untuk demo");
...
}

β οΈ Catatan Penting
error.tsxtidak otomatis "sembuh" ketika user navigasi. Makanya, perlu tombol "Coba Lagi" yang memanggilreset().- Jangan taruh logic berat di dalam file ini. Keep it simple and informative.
- Cocok digabung dengan alat monitoring seperti Sentry untuk log error secara real-time.
Dengan error.tsx, kamu gak cuma nangkap error β kamu juuga menyelamatkan pengalaman pengguna dengan cara yang anggun. Web kamu tetap terlihat profesional, bahkan ketika sesuatu tidak berjalan sesuai rencana. π
Mantap! Sekarang kita ubah bagian studi kasus jadi tutorial proyek Next.js lengkap dengan struktur folder yang kamu kasih. Gaya tetap santai, storytelling, dan teknikal step-by-step. Cocok banget buat developer yang mau langsung praktik.
π§ Tutorial Proyek Next.js: Portal Produk dengan Layouting Modern
Kita akan bangun proyek portal produk sederhana dengan Next.js 15, lengkap dengan fitur:
- Animasi layout per navigasi via
template.tsx - Halaman 404 per produk via
not-found.tsx - Penanganan error API via
error.tsx - Routing dinamis berdasarkan kategori
- Komponen modular + UI dari Shadcn
π Langkah 1: Inisialisasi Proyek
npx create-next-app@latest portal-produk --ts --app
cd portal-produk
π Langkah 2: Setup Tailwind + Shadcn
npx shadcn@latest init
Lalu install beberapa komponen dasar:
npx shadcn@latest add button
π§± Struktur Proyek
Berikut struktur yang kita bangun di folder src/:
src/
ββ actions/
β ββ category/getAll.ts
β ββ product/
β ββ getAll.ts
β ββ getByCategory.ts
β ββ getProduct.ts
ββ app/
β ββ error.tsx
β ββ favicon.ico
β ββ globals.css
β ββ layout.tsx
β ββ page.tsx
β ββ product/
β ββ page.tsx
β ββ template.tsx
β ββ [id]/
β ββ page.tsx
β ββ not-found.tsx
ββ components/
β ββ ui/button.tsx
β ββ category.tsx
β ββ category-list.tsx
β ββ product-card.tsx
β ββ product-list.tsx
ββ lib/
β ββ endpoint.ts
β ββ utils.ts
ββ types/
ββ category.ts
ββ product.ts
π Routing: src/app/product/page.tsx
Ini halaman utama produk, menampilkan daftar kategori dan produk berdasarkan search params.
// src/app/product/page.tsx
import { CategoryList } from "@/components/category-list";
import { ProductList } from "@/components/product-list";
import { Suspense } from "react";
interface ProductPageProps {
searchParams: Promise<{
kategori?: string;
}>;
}
export default async function ProductPage({ searchParams }: ProductPageProps) {
// throw new Error("Simulasi error untuk demo");
const { kategori } = await searchParams;
return (
<div>
<Suspense fallback={<p>Loading...</p>}>
<CategoryList />
</Suspense>
<Suspense fallback={<p>Loading product..</p>}>
<ProductList kategori={kategori} />
</Suspense>
</div>
);
}
π Animasi Navigasi: template.tsx
// scr/app/product/template.tsx
export default function Template({ children }: { children: React.ReactNode }) {
return <div className="animate-fade-in min-h-screen">{children}</div>;
}
Tambahkan animasi fade-in di globals.css:
@theme inline {
...
--animate-fade-in: fadein 1s ease-in;
@keyframes fadein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
}
β Produk Gak Ditemukan: not-found.tsx
// src/app/product/[id]/not-found.tsx
export default function NotFound() {
return (
<div className="text-center py-20">
<h1 className="text-4xl font-bold text-red-600">
Produk tidak ditemukan
</h1>
<p className="mt-4 text-gray-500">Coba cari produk lainnya ya π</p>
</div>
);
}
// src/app/product/[id]/page.tsx
import { getProduct } from "@/actions/product/getProduct";
import { notFound } from "next/navigation";
import Image from "next/image";
interface DetailPageProps {
params: Promise<{
id: string;
}>;
}
export default async function DetailPage({ params }: DetailPageProps) {
const { id } = await params;
const product = await getProduct(id);
console.log("product:", product);
if (!product) return notFound();
return (
<div className="max-w-4xl mx-auto px-4 py-8">
<div className="flex flex-col md:flex-row gap-8">
<Image
src={product.thumbnail}
alt={product.title}
width={400}
height={400}
className="object-cover rounded-md shadow-md"
/>
<div className="flex-1 space-y-4">
<h1 className="text-3xl font-bold">{product.title}</h1>
<p className="text-gray-600">{product.description}</p>
<div className="text-xl font-semibold text-green-600">
${product.price}{" "}
<span className="text-sm text-gray-500 line-through ml-2">
$
{Math.round(
product.price / (1 - product.discountPercentage / 100)
)}
</span>
<span className="ml-2 text-sm text-red-500">
-{product.discountPercentage}%
</span>
</div>
<div className="flex items-center gap-2">
<span className="text-yellow-500">β {product.rating}</span>
<span className="text-sm text-gray-500">
({product.stock} stock)
</span>
</div>
<div className="text-sm text-gray-500">
<p>
<strong>Brand:</strong> {product.brand}
</p>
<p>
<strong>Category:</strong> {product.category}
</p>
<p>
<strong>SKU:</strong> {product.sku}
</p>
<p>
<strong>Weight:</strong> {product.weight} g
</p>
<p>
<strong>Dimensions:</strong> {product.dimensions.width}Γ
{product.dimensions.height}Γ{product.dimensions.depth} cm
</p>
</div>
<div>
<strong className="text-sm">Tags:</strong>
<div className="flex flex-wrap gap-2 mt-1">
{product.tags.map((tag) => (
<span
key={tag}
className="px-2 py-1 bg-gray-200 text-sm rounded-md"
>
#{tag}
</span>
))}
</div>
</div>
</div>
</div>
</div>
);
}
π₯ Penanganan Error: src/app/error.tsx
"use client";
import { useEffect } from "react";
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
useEffect(() => {
// Kirim ke log monitoring kalau perlu
console.error("Terjadi error:", error);
}, [error]);
return (
<div className="p-10 text-center">
<h2 className="text-2xl font-semibold text-red-600">
Ups! Ada error nih π§¨
</h2>
<p className="mt-4 text-gray-500">{error.message}</p>
<button
onClick={reset}
className="mt-6 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Coba Lagi
</button>
</div>
);
}
Untuk testing, tinggal lempar error di halaman mana pun:
throw new Error("Simulasi error dari API");
βοΈ Action Functions: Sumber Data Produk & Kategori
Semua action akan kita simpan di dalam src/actions, dipisah berdasarkan modul. Action ini bertugas fetch data dari API eksternal (atau local endpoint), dan bisa langsung dipakai di server component.
π src/actions/category/getAll.ts
Ambil semua kategori produk.
// src/actions/category/getAll.ts
"use server";
import { Category } from "@/types/category";
import { urlAllCategories } from "@/lib/endpoint";
export async function getAllCategories(): Promise<Category[]> {
const data = await fetch(urlAllCategories, { cache: "no-store" });
return await data.json();
}
Gunakan cache: "no-store" supaya datanya selalu fresh setiap halaman dibuka.
π src/actions/product/getAll.ts
Ambil semua produk.
// src/actions/product/getAll.ts
"use server";
import { ProductList } from "@/types/product";
import { urlAllProduct } from "@/lib/endpoint";
export async function getAllProducts(): Promise<ProductList> {
const data = await fetch(urlAllProduct, { cache: "no-store" });
return await data.json();
}
π src/actions/product/getByCategory.ts
Ambil produk berdasarkan kategori.
// src/actions/product/getByCategory.ts
"use server";
import { ProductList } from "@/types/product";
import { urlProductByCategory } from "@/lib/endpoint";
export async function getProductByCategory(
category: string
): Promise<ProductList> {
const data = await fetch(urlProductByCategory(category), {
cache: "no-store",
});
return await data.json();
}
Kamu bisa panggil ini dari ProductList dan lempar kategori dari searchParams.
π src/actions/product/getProduct.ts
Ambil detail produk berdasarkan ID.
// src/actions/product/getProduct.ts
"use server";
import { urlSingleProduct } from "@/lib/endpoint";
import { Product } from "@/types/product";
export async function getProduct(id: string): Promise<Product | null> {
const data = await fetch(urlSingleProduct(id), { cache: "no-store" });
const body = await data.json();
if (body.message) {
console.log("msg:", body.message);
return null;
}
return body;
}
π§Ύ Tipe Data: Biar Konsisten dan Tipe-Safe
Semua file tipe kita taruh di src/types supaya bisa dipakai ulang di mana pun.
π src/types/category.ts
// src/types/category.ts
export interface Category {
slug: string;
name: string;
ulr: string;
}
π src/types/product.ts
// src/types/product.ts
export interface Product {
id: number;
title: string;
description: string;
category: string;
price: number;
discountPercentage: number;
rating: number;
stock: number;
tags: string[];
brand: string;
sku: string;
weight: number;
dimensions: {
width: number;
height: number;
depth: number;
};
thumbnail: string;
}
export interface ProductList {
products: Product[];
total: number;
skip: number;
limit: number;
}
Kamu bisa menambahkan field opsional lainnya sesuai kebutuhan UI (misalnya rating, stock, dsb).
π¦ src/lib/endpoint.ts
Pusatkan base URL API di satu tempat:
// src/lib/endpoint.ts
export const urlAllProduct = "<https://dummyjson.com/products>";
export const urlAllCategories = `${urlAllProduct}/categories`;
export const urlSingleProduct = (id: string) => `${urlAllProduct}/${id}`;
export const urlProductByCategory = (category: string) =>
`${urlAllProduct}/category/${category}`;
π¦ src/lib/utils.ts
Kalau nanti butuh helper function (misalnya format harga), kamu bisa taruh di sini. Contoh:
// src/lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Hasilnya

π‘ Tips & Best Practice
Setelah semua implementasi, ada beberapa hal penting yang sering terlupakan tapi bisa bikin proyek kamu makin solid dan UX-nya makin kece:
- β
Gunakan
template.tsxsaat butuh efek baru per navigasi Misalnya untuk animasi transisi antar kategori, efek acak background, atau reset layout tertentu setiap pindah halaman. - β
Letakkan
not-found.tsxdi folder yang punya kemungkinan akses dinamis Seperti[id],[slug], atau[category]. Ini bikin 404 kamu lebih kontekstual, dan gak perlu global fallback yang terlalu umum. - β οΈ Jangan lupa
error.tsxharus pakai'use client'! Karena file ini butuh akses keuseEffectdan fungsireset(), tanpa directive ini error handler kamu gak bakal jalan. - π Test skenario edge-case Coba akses URL secara manual (misalnya lewat address bar) atau deep link dari luar. Ini penting banget buat pastikan
notFound()danerror.tsxmerespons dengan baik.
Degan kombinasi layouting modular dan error handling yang bijak, kamu bisa bikin aplikasi Next.js yang gak cuma cepat dan efisien, tapi juga siap menghadapi ketidaksempurnaan dunia nyata. β¨
Penutup
Dengan memahami bagaimana cara kerja template.tsx, not-found.tsx, dan error.tsx di Next.js 15, kita bisa membangun layout yang lebih kontekstual, adaptif, dan siap menghadapi berbagai skenario nyata dalam pengembangan web. Fitur-fitur ini memberikan fleksibilitas dan kontrol yang lebih besar terhadap perilaku UI, tidak hanya sekadar urusan tampilan, tapi juga pengalaman pengguna secara keseluruhan.
Jika Anda ingin meningkatkan kemampuan coding lebih lanjut dan belajar secara mendalam tentang Next.js maupun pengembangan web modern lainnya, Anda bisa belajar bersama mentor expert di BuildWithAngga.
Selain mendapatkan akses belajar seumur hidup, Anda juga berkesempatan membangun portfolio berkualitas, konsultasi langsung dengan mentor, serta mendapatkan berbagai benefit menarik lainnya yang akan membantu Anda menjadi web developer profesional.
β¨ Mari bergabung dan wujudkan karier impian Anda bersama BuildWithAngga!