DEV Community

MATKARIM MATKARIMOV
MATKARIM MATKARIMOV

Posted on

Next.js - File Conventions (Fayl Konventsiyalari) To'liq Qo'llanma

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
Enter fullscreen mode Exit fullscreen mode

Render tartibi:

<Layout>
  <Template>
    <ErrorBoundary fallback={<Error />}>
      <Suspense fallback={<Loading />}>
        <NotFoundBoundary fallback={<NotFound />}>
          <Page />
        </NotFoundBoundary>
      </Suspense>
    </ErrorBoundary>
  </Template>
</Layout>
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode

Muhim: Next.js 15+ da params va searchParams Promise. Server Component da await, Client Component da use() 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode
  • <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>;
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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 },
};
Enter fullscreen mode Exit fullscreen mode
// app/global-not-found.tsx
export default function GlobalNotFound() {
  return (
    <html lang="uz">
      <body>
        <h1>404 - Sahifa topilmadi</h1>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode

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 useEffect qayta 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Render tartibi

<Layout>
  {/* Template unique key oladi - segment o'zgarganda remount */}
  <Template key={routeParam}>
    {children}
  </Template>
</Layout>
Enter fullscreen mode Exit fullscreen mode

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 });
}
Enter fullscreen mode Exit fullscreen mode

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) {}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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 });
}
Enter fullscreen mode Exit fullscreen mode

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 });
}
Enter fullscreen mode Exit fullscreen mode

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 });
}
Enter fullscreen mode Exit fullscreen mode

Redirect

import { redirect } from 'next/navigation';

export async function GET() {
  redirect('https://nextjs.org/');
}
Enter fullscreen mode Exit fullscreen mode

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',
    },
  });
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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 });
}
Enter fullscreen mode Exit fullscreen mode

Muhim: page.tsx va route.ts bitta 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>;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Foydalanuvchi /settings ga kirib sahifani yangilasa:

  • @teamsettings/page.tsx ko'rsatadi
  • @analyticssettings/ yo'q → default.tsx ko'rsatadi

Muhim: default.tsx bo'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>
  );
}
Enter fullscreen mode Exit fullscreen mode
// 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>;
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode
// 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>;
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Muhim: cacheComponents: true yoqilganda, bu config lar o'rniga use cache + cacheLife ishlatiladi.


11. Dinamik Route Konventsiyalari

Oddiy dinamik segment

app/blog/[slug]/page.tsx      →  /blog/hello-world
Enter fullscreen mode Exit fullscreen mode

Catch-all segment

app/shop/[...slug]/page.tsx   →  /shop/a, /shop/a/b, /shop/a/b/c
Enter fullscreen mode Exit fullscreen mode

Optional catch-all segment

app/docs/[[...slug]]/page.tsx →  /docs, /docs/a, /docs/a/b
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
export default function Layout({
  children,
  analytics,
  team,
}: {
  children: React.ReactNode;
  analytics: React.ReactNode;
  team: React.ReactNode;
}) {
  return (
    <>
      {children}
      <aside>{analytics}</aside>
      <section>{team}</section>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Intercepting Routes

(.)folder    →  shu darajadagi route ni ushlash
(..)folder   →  bir daraja yuqoridagi
(..)(..)folder → ikki daraja yuqoridagi
(...)folder  →  root dan ushlash
Enter fullscreen mode Exit fullscreen mode

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

  1. page.tsx bo'lmasa, papka route emas
  2. page.tsx va route.ts bitta papkada birga bo'lmaydi
  3. error.tsx har doim 'use client' bo'lishi shart
  4. layout.tsx navigatsiyada qayta renderlanmaydi - state saqlanadi
  5. template.tsx har navigatsiyada qayta renderlanadi - state resetlanadi
  6. loading.tsx avtomatik <Suspense> boundary yaratadi
  7. Parallel route larda har doim default.tsx qo'shing
  8. Next.js 15+ da params va searchParams Promise - await yoki use() kerak

Bu maqola Next.js 15+ rasmiy dokumentatsiyasi asosida yozilgan. Savollar yoki takliflar bo'lsa, kommentariyada yozing!
(https://www.matkarim.uz)

Top comments (0)