Next.js 15+ da barcha maxsus fayllar - page, layout, loading, error, not-found, route, template, default, forbidden, unauthorized va route segment config
Next.js App Router - File Conventions (Fayl Konventsiyalari)
Next.js app/ papkasi ichida maxsus nomli fayllarni taniydi. Har bir fayl aniq vazifaga ega va Next.js uni avtomatik ravishda to'g'ri joyga render qiladi.
Bu maqolada barcha maxsus fayllarni batafsil misollar bilan tushuntirib beraman.
Umumiy ko'rinish - Barcha maxsus fayllar
app/
├── layout.tsx # Umumiy layout (qayta renderlanmaydi)
├── template.tsx # Layout kabi, lekin HAR SAFAR qayta renderlanadi
├── page.tsx # Sahifa - route ni public qiladi
├── loading.tsx # Yuklanish holati (Suspense fallback)
├── error.tsx # Xatolik holati (Error Boundary)
├── global-error.tsx # Global xatolik (root layout uchun)
├── not-found.tsx # 404 sahifa
├── global-not-found.tsx # Global 404 (experimental)
├── forbidden.tsx # 403 sahifa (experimental)
├── unauthorized.tsx # 401 sahifa (experimental)
├── route.ts # API endpoint (page bilan birga bo'lmaydi!)
├── default.tsx # Parallel route fallback
├── [slug]/ # Dinamik segment
├── (group)/ # Route group
└── @slot/ # Parallel route slot
Render tartibi:
<Layout>
<Template>
<ErrorBoundary fallback={<Error />}>
<Suspense fallback={<Loading />}>
<NotFoundBoundary fallback={<NotFound />}>
<Page />
</NotFoundBoundary>
</Suspense>
</ErrorBoundary>
</Template>
</Layout>
1. page.tsx - Sahifa
Route ni ommaviy (public) qiladi. Bu faylsiz papka route bo'lmaydi.
// app/blog/[slug]/page.tsx
export default async function Page({
params,
searchParams,
}: {
params: Promise<{ slug: string }>;
searchParams: Promise<{
[key: string]: string | string[] | undefined;
}>;
}) {
const { slug } = await params;
const { page = '1', sort = 'asc' } = await searchParams;
return (
<div>
<h1>Blog Post: {slug}</h1>
<p>Sahifa: {page}, Tartib: {sort}</p>
</div>
);
}
Props
| Prop | Turi | Tavsif |
|---|---|---|
params |
Promise<{...}> |
Dinamik route parametrlari |
searchParams |
Promise<{...}> |
URL query parametrlari (?key=value) |
params misollari
| Route | URL | params |
|---|---|---|
app/shop/[slug]/page.tsx |
/shop/shoes |
{ slug: 'shoes' } |
app/shop/[category]/[item]/page.tsx |
/shop/clothes/shirt |
{ category: 'clothes', item: 'shirt' } |
app/shop/[...slug]/page.tsx |
/shop/a/b/c |
{ slug: ['a', 'b', 'c'] } |
searchParams misollari
| URL | searchParams |
|---|---|
/shop?a=1 |
{ a: '1' } |
/shop?a=1&b=2 |
{ a: '1', b: '2' } |
/shop?a=1&a=2 |
{ a: ['1', '2'] } |
PageProps helper (TypeScript)
Import kerak emas - global mavjud:
export default async function Page(
props: PageProps<'/blog/[slug]'>
) {
const { slug } = await props.params;
return <h1>{slug}</h1>;
}
Client Component da use() ishlatish
'use client';
import { use } from 'react';
export default function Page({
params,
searchParams,
}: {
params: Promise<{ slug: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const { slug } = use(params); // await emas, use()!
const { query } = use(searchParams);
return <h1>{slug} - {query}</h1>;
}
Muhim: Next.js 15+ da
paramsvasearchParamsPromise. Server Component daawait, Client Component dause()ishlatiladi.
2. layout.tsx - Layout
Bir nechta sahifalar orasida umumiy UI. Navigatsiyada qayta renderlanmaydi, state saqlanadi.
// app/dashboard/layout.tsx
export default async function DashboardLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ team: string }>;
}) {
const { team } = await params;
return (
<section>
<header>
<h1>{team} Dashboard</h1>
</header>
<main>{children}</main>
</section>
);
}
Props
| Prop | Turi | Tavsif |
|---|---|---|
children |
React.ReactNode |
Majburiy - sahifa yoki ichki layout |
params |
Promise<{...}> |
Ixtiyoriy - dinamik route parametrlari |
Root Layout - MAJBURIY
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="uz">
<body>{children}</body>
</html>
);
}
-
<html>va<body>bo'lishi shart -
<head>ni qo'lda qo'shmang - Metadata API ishlatang
LayoutProps helper
export default function Layout(
props: LayoutProps<'/dashboard'>
) {
return <section>{props.children}</section>;
}
Layout ning cheklovlari
Layout navigatsiyada qayta renderlanmaydi, shu sababli:
| Muammo | Yechim |
|---|---|
searchParams ga kirish yo'q |
useSearchParams() hook (Client Component) |
pathname ga kirish yo'q |
usePathname() hook (Client Component) |
| Child segment ni bilish yo'q |
useSelectedLayoutSegment() hook |
| Children ga data uzatish yo'q | Bir xil fetch - React avtomatik deduplicate qiladi |
Aktiv navigatsiya misoli:
'use client';
import { usePathname } from 'next/navigation';
import Link from 'next/link';
export function NavLinks() {
const pathname = usePathname();
return (
<nav>
<Link
href="/"
className={pathname === '/' ? 'active' : ''}
>
Bosh sahifa
</Link>
<Link
href="/about"
className={pathname === '/about' ? 'active' : ''}
>
Biz haqimizda
</Link>
</nav>
);
}
Child segment ni aniqlash:
'use client';
import { useSelectedLayoutSegment } from 'next/navigation';
export function NavLink({
slug,
children,
}: {
slug: string;
children: React.ReactNode;
}) {
const segment = useSelectedLayoutSegment();
const isActive = slug === segment;
return (
<Link
href={`/blog/${slug}`}
style={{ fontWeight: isActive ? 'bold' : 'normal' }}
>
{children}
</Link>
);
}
3. loading.tsx - Yuklanish holati
page.tsx ni avtomatik <Suspense> bilan o'rab, yuklanish UI ko'rsatadi.
// app/dashboard/loading.tsx
export default function Loading() {
return (
<div className="animate-pulse space-y-4">
<div className="h-8 bg-gray-200 rounded w-1/3" />
<div className="h-4 bg-gray-200 rounded w-full" />
<div className="h-4 bg-gray-200 rounded w-2/3" />
</div>
);
}
Xususiyatlari
- Hech qanday props qabul qilmaydi
- Default holatda Server Component
- Prefetching da fallback UI yuklanadi - navigatsiya darhol bo'ladi
- Layout interaktiv bo'lib qoladi
- Navigatsiya interruptible - boshqa linkni bosish mumkin
Ichkarida nima bo'layotgani
// Next.js avtomatik shunday qiladi:
<Layout>
<Suspense fallback={<Loading />}>
<Page />
</Suspense>
</Layout>
Qo'lda Suspense ishlatish
Sahifa ichida alohida bo'limlar uchun:
import { Suspense } from 'react';
export default function Page() {
return (
<section>
<Suspense fallback={<p>Feed yuklanmoqda...</p>}>
<PostFeed />
</Suspense>
<Suspense fallback={<p>Ob-havo yuklanmoqda...</p>}>
<Weather />
</Suspense>
</section>
);
}
// PostFeed va Weather PARALLEL yuklanadi!
4. error.tsx - Xatolik holati
Route segment ni Error Boundary bilan o'rab, xatolik UI ko'rsatadi.
Majburiy: 'use client' bo'lishi shart!
// app/dashboard/error.tsx
'use client';
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Xatolikni logging servisga yuborish
console.error(error);
}, [error]);
return (
<div>
<h2>Xatolik yuz berdi!</h2>
<p>{error.message}</p>
<button onClick={() => reset()}>
Qayta urinish
</button>
</div>
);
}
Props
| Prop | Turi | Tavsif |
|---|---|---|
error |
Error & { digest?: string } |
Xatolik obyekti |
error.message |
string |
Dev da to'liq, prod da umumiy xabar |
error.digest |
string |
Server loglarida moslashtirish uchun hash |
reset |
() => void |
Error boundary ni qayta render qilish |
global-error.tsx - Root layout xatoliklari uchun
Root layout dagi xatoliklarni ushlaydi. O'z <html> va <body> teglari bo'lishi shart:
// app/global-error.tsx
'use client';
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<html>
<body>
<h2>Jiddiy xatolik!</h2>
<button onClick={() => reset()}>Qayta urinish</button>
</body>
</html>
);
}
5. not-found.tsx - 404 sahifa
notFound() funksiyasi chaqirilganda ko'rsatiladigan UI:
// app/not-found.tsx
import Link from 'next/link';
export default function NotFound() {
return (
<div>
<h2>Topilmadi</h2>
<p>So'ralgan resurs topilmadi</p>
<Link href="/">Bosh sahifaga qaytish</Link>
</div>
);
}
Async bo'lishi mumkin
import Link from 'next/link';
import { headers } from 'next/headers';
export default async function NotFound() {
const headersList = await headers();
const domain = headersList.get('host');
const data = await getSiteData(domain);
return (
<div>
<h2>Topilmadi: {data.name}</h2>
<Link href="/blog">Barcha postlar</Link>
</div>
);
}
global-not-found.tsx (Experimental v15.4+)
Butun ilova uchun 404. Ko'p root layout bo'lganda kerak:
// next.config.ts
const nextConfig = {
experimental: { globalNotFound: true },
};
// app/global-not-found.tsx
export default function GlobalNotFound() {
return (
<html lang="uz">
<body>
<h1>404 - Sahifa topilmadi</h1>
</body>
</html>
);
}
6. template.tsx - Template (qayta renderlash)
Layout kabi, lekin navigatsiyada har safar qayta renderlanadi. State resetlanadi, useEffect qayta ishlaydi:
// app/dashboard/template.tsx
export default function Template({
children,
}: {
children: React.ReactNode;
}) {
return <div>{children}</div>;
}
Layout vs Template
| Xususiyat | Layout | Template |
|---|---|---|
| Navigatsiyada qayta render | Yo'q | Ha |
| State saqlanishi | Ha | Yo'q (resetlanadi) |
| useEffect qayta ishlashi | Yo'q | Ha |
| DOM qayta yaratilishi | Yo'q | Ha |
Qachon Template ishlatish kerak?
- Har navigatsiyada
useEffectqayta ishlashi kerak bo'lganda - Child komponentlar state ni resetlash kerak bo'lganda
- Suspense fallback ni har navigatsiyada ko'rsatish kerak bo'lganda
// Misol: Har sahifaga o'tganda animatsiya
export default function Template({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="animate-fadeIn">
{children}
</div>
);
}
Render tartibi
<Layout>
{/* Template unique key oladi - segment o'zgarganda remount */}
<Template key={routeParam}>
{children}
</Template>
</Layout>
7. route.ts - API Endpoint (Route Handler)
Web Request/Response API yordamida backend API yaratish:
// app/api/users/route.ts
export async function GET() {
const users = await db.users.findMany();
return Response.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const user = await db.users.create({ data: body });
return Response.json(user, { status: 201 });
}
Qo'llab-quvvatlanadigan HTTP metodlar
export async function GET(request: Request) {}
export async function POST(request: Request) {}
export async function PUT(request: Request) {}
export async function PATCH(request: Request) {}
export async function DELETE(request: Request) {}
export async function HEAD(request: Request) {}
export async function OPTIONS(request: Request) {}
Dinamik route parametrlari
// app/api/users/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
const user = await db.users.findUnique({ where: { id } });
if (!user) {
return Response.json(
{ error: 'Topilmadi' },
{ status: 404 }
);
}
return Response.json(user);
}
Cookies va Headers
import { cookies, headers } from 'next/headers';
export async function GET() {
const cookieStore = await cookies();
const token = cookieStore.get('token');
const headersList = await headers();
const referer = headersList.get('referer');
return Response.json({ token: token?.value, referer });
}
Query parametrlari
import { type NextRequest } from 'next/server';
export function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const query = searchParams.get('query');
// /api/search?query=hello → query = "hello"
return Response.json({ query });
}
FormData
export async function POST(request: Request) {
const formData = await request.formData();
const name = formData.get('name');
const email = formData.get('email');
return Response.json({ name, email });
}
Redirect
import { redirect } from 'next/navigation';
export async function GET() {
redirect('https://nextjs.org/');
}
CORS
export async function GET() {
return new Response('OK', {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}
Streaming
const encoder = new TextEncoder();
async function* makeIterator() {
yield encoder.encode('Birinchi qism\n');
await new Promise(r => setTimeout(r, 500));
yield encoder.encode('Ikkinchi qism\n');
await new Promise(r => setTimeout(r, 500));
yield encoder.encode('Uchinchi qism\n');
}
export async function GET() {
const iterator = makeIterator();
const stream = new ReadableStream({
async pull(controller) {
const { value, done } = await iterator.next();
if (done) controller.close();
else controller.enqueue(value);
},
});
return new Response(stream);
}
RouteContext helper (TypeScript)
import type { NextRequest } from 'next/server';
export async function GET(
_req: NextRequest,
ctx: RouteContext<'/users/[id]'>
) {
const { id } = await ctx.params;
return Response.json({ id });
}
Muhim:
page.tsxvaroute.tsbitta papkada birga bo'lishi mumkin emas!
8. default.tsx - Parallel Route Fallback
Parallel Routes da to'liq sahifa yuklanishidan keyin slot ning aktiv holatini tiklash mumkin bo'lmaganda fallback sifatida renderlanadi:
// app/@analytics/default.tsx
export default function Default() {
return <div>Analytics yuklanmoqda...</div>;
}
Qachon kerak?
app/
├── layout.tsx # @team va @analytics ni render qiladi
├── page.tsx
├── @team/
│ ├── page.tsx # /
│ ├── settings/
│ │ └── page.tsx # /settings
│ └── default.tsx # Fallback
└── @analytics/
├── page.tsx # /
└── default.tsx # /settings da analytics yo'q → default ko'rsatiladi
Foydalanuvchi /settings ga kirib sahifani yangilasa:
-
@team→settings/page.tsxko'rsatadi -
@analytics→settings/yo'q →default.tsxko'rsatadi
Muhim:
default.tsxbo'lmasa, xatolik chiqadi! Parallel route ishlatganda har doim qo'shing.
9. forbidden.tsx va unauthorized.tsx (Experimental)
Autentifikatsiya uchun yangi maxsus fayllar:
forbidden.tsx - 403 (Taqiqlangan)
// app/forbidden.tsx
import Link from 'next/link';
export default function Forbidden() {
return (
<div>
<h2>403 - Taqiqlangan</h2>
<p>Bu resursga kirishga ruxsatingiz yo'q.</p>
<Link href="/">Bosh sahifaga</Link>
</div>
);
}
// Ishlatish - page.tsx da
import { forbidden } from 'next/navigation';
export default async function AdminPage() {
const user = await getUser();
if (user.role !== 'ADMIN') {
forbidden(); // 403 status + forbidden.tsx renderlanadi
}
return <div>Admin Panel</div>;
}
unauthorized.tsx - 401 (Autentifikatsiya talab qilinadi)
// app/unauthorized.tsx
import Login from '@/components/Login';
export default function Unauthorized() {
return (
<div>
<h1>401 - Tizimga kiring</h1>
<p>Bu sahifani ko'rish uchun tizimga kirishingiz kerak.</p>
<Login />
</div>
);
}
// Ishlatish
import { unauthorized } from 'next/navigation';
export default async function DashboardPage() {
const session = await verifySession();
if (!session) {
unauthorized(); // 401 status + unauthorized.tsx renderlanadi
}
return <div>Dashboard</div>;
}
Eslatma: Bu ikkalasi hali experimental - production da ehtiyot bo'ling.
10. Route Segment Config
Sahifa yoki layout ning rendering xatti-harakatini sozlash:
// Har qanday page.tsx yoki layout.tsx da
export const dynamic = 'auto';
export const dynamicParams = true;
export const revalidate = false;
export const fetchCache = 'auto';
export const runtime = 'nodejs';
export const preferredRegion = 'auto';
export const maxDuration = 5;
Har bir option tushuntirilishi
dynamic
| Qiymat | Ma'nosi |
|---|---|
'auto' |
Default - Next.js o'zi aniqlaydi |
'force-dynamic' |
Har safar dinamik render (SSR) |
'force-static' |
Majburiy statik (cookies/headers bo'sh qaytadi) |
'error' |
Statik, dinamik API ishlatilsa xatolik |
dynamicParams
| Qiymat | Ma'nosi |
|---|---|
true |
generateStaticParams da yo'q segmentlar on-demand generatsiya |
false |
Yo'q segmentlar 404 qaytaradi |
revalidate
| Qiymat | Ma'nosi |
|---|---|
false |
Cheksiz kesh (default) |
0 |
Har safar dinamik render |
60 |
60 soniyada bir qayta tekshirish |
runtime
| Qiymat | Ma'nosi |
|---|---|
'nodejs' |
Node.js runtime (default) |
'edge' |
Edge Runtime (Cache Components bilan ishlamaydi) |
maxDuration
export const maxDuration = 30; // Server funksiyasi max 30 soniya ishlaydi
Muhim:
cacheComponents: trueyoqilganda, bu config lar o'rnigause cache+cacheLifeishlatiladi.
11. Dinamik Route Konventsiyalari
Oddiy dinamik segment
app/blog/[slug]/page.tsx → /blog/hello-world
Catch-all segment
app/shop/[...slug]/page.tsx → /shop/a, /shop/a/b, /shop/a/b/c
Optional catch-all segment
app/docs/[[...slug]]/page.tsx → /docs, /docs/a, /docs/a/b
12. Route Group va Parallel Route Konventsiyalari
Route Group - (folder)
URL da ko'rinmaydi, tartibga solish uchun:
app/(marketing)/about/page.tsx → /about
app/(shop)/cart/page.tsx → /cart
Parallel Route - @slot
Bitta layout da bir nechta sahifani parallel ko'rsatish:
app/
├── layout.tsx # children + @analytics + @team
├── @analytics/
│ ├── page.tsx
│ └── default.tsx
├── @team/
│ ├── page.tsx
│ └── default.tsx
└── page.tsx
export default function Layout({
children,
analytics,
team,
}: {
children: React.ReactNode;
analytics: React.ReactNode;
team: React.ReactNode;
}) {
return (
<>
{children}
<aside>{analytics}</aside>
<section>{team}</section>
</>
);
}
Intercepting Routes
(.)folder → shu darajadagi route ni ushlash
(..)folder → bir daraja yuqoridagi
(..)(..)folder → ikki daraja yuqoridagi
(...)folder → root dan ushlash
Xulosa jadvali
| Fayl | Vazifasi |
'use client' kerakmi |
Props |
|---|---|---|---|
page.tsx |
Sahifa UI | Yo'q (ixtiyoriy) |
params, searchParams
|
layout.tsx |
Umumiy UI (qayta renderlanmaydi) | Yo'q |
children, params
|
template.tsx |
Umumiy UI (HAR SAFAR renderlanadi) | Yo'q | children |
loading.tsx |
Yuklanish skeleton | Yo'q | Hech narsa |
error.tsx |
Xatolik UI | Ha, majburiy |
error, reset
|
global-error.tsx |
Root xatolik UI | Ha, majburiy |
error, reset
|
not-found.tsx |
404 UI | Yo'q | Hech narsa |
forbidden.tsx |
403 UI (experimental) | Yo'q | Hech narsa |
unauthorized.tsx |
401 UI (experimental) | Yo'q | Hech narsa |
route.ts |
API endpoint | Yo'q |
request, context
|
default.tsx |
Parallel route fallback | Yo'q | params |
Oltin qoidalar
-
page.tsxbo'lmasa, papka route emas -
page.tsxvaroute.tsbitta papkada birga bo'lmaydi -
error.tsxhar doim'use client'bo'lishi shart -
layout.tsxnavigatsiyada qayta renderlanmaydi - state saqlanadi -
template.tsxhar navigatsiyada qayta renderlanadi - state resetlanadi -
loading.tsxavtomatik<Suspense>boundary yaratadi -
Parallel route larda har doim
default.tsxqo'shing - Next.js 15+ da
paramsvasearchParamsPromise -awaityokiuse()kerak
Bu maqola Next.js 15+ rasmiy dokumentatsiyasi asosida yozilgan. Savollar yoki takliflar bo'lsa, kommentariyada yozing!
(https://www.matkarim.uz)
Top comments (0)