---
title: "Page Nav"
description: "Birden çok bölümü olan uygulamalarda sayfanın en üstündeki bölüm çubuğu. Aktif bölümün altındaki çizgi seçime doğru kayar."
---

<Callout type="info" title="Ne zaman kullanılır">
Uygulamanızın birden çok bölümü varsa (Genel bakış, Gönderimler, Ayarlar gibi) ve bunlar arasında geçiş gerekiyorsa. Uygulama, ikas panelinin kendi üst barının altında açılır; geri butonu, logo, uygulama adı ve "Planı yönet" zaten oradadır. Bu yüzden kendi üst barınızı veya sidebar'ınızı çizmeyin, bölümleri `PageNav` ile ayırın.
</Callout>

## Kurulum

```bash
npx shadcn@latest add @ikas/page-nav
```

Registry tanımı yoksa önce [Kurulum](https://builders.ikas.com/tr/docs/app-development/ui-kit/installation) adımlarını tamamlayın.

## Örnekler

### Sekmeler

Tek sayfalık uygulamalar için. `onValueChange` verdiğinizde öğeler sekme olur ve bölüm sayfa yenilenmeden değişir. `badge`, bölümde bekleyen iş sayısını gösterir.

**Örnek: Sekmeler** (`page-nav/basic`) · [Canlı önizleme](https://builders.ikas.com/ui-preview/demo/page-nav/basic)

```tsx
import * as React from "react"

import { Card, CardContent } from "@/components/ui/card"
import { Page, PageHeader } from "@/components/ikas/page"
import { PageNav } from "@/components/ikas/page-nav"

const sections = {
  overview: { label: "Genel bakış", text: "Bu ayki gönderimler ve kalan etiket hakkı." },
  shipments: { label: "Gönderimler", text: "Kargoya verilen ve etiket bekleyen gönderimler." },
  rules: { label: "Kurallar", text: "Hangi siparişin hangi kargo firmasına gideceği." },
  settings: { label: "Ayarlar", text: "Kargo hesapları ve etiket boyutu." },
} as const

type Section = keyof typeof sections

export default function PageNavBasic() {
  const [section, setSection] = React.useState<Section>("shipments")

  return (
    <Page width="full" className="py-0">
      <PageNav
        value={section}
        onValueChange={setSection}
        items={[
          { value: "overview", label: sections.overview.label },
          { value: "shipments", label: sections.shipments.label, badge: 3 },
          { value: "rules", label: sections.rules.label },
          { value: "settings", label: sections.settings.label },
        ]}
      />
      <PageHeader title={sections[section].label} description={sections[section].text} />
      <Card>
        <CardContent className="text-sm text-muted-foreground">{sections[section].label} bölümünün içeriği.</CardContent>
      </Card>
    </Page>
  )
}
```

### Panelin içinde

`PageNav`, panelin üst barının hemen altında, `Page`'in ilk çocuğu olarak durur. Örnekteki üst bar yalnızca bağlamı göstermek için çizildi; uygulamanız onu çizmez.

**Örnek: Panelin içinde** (`page-nav/in-panel`) · [Canlı önizleme](https://builders.ikas.com/ui-preview/demo/page-nav/in-panel)

```tsx
import * as React from "react"
import { ArrowLeftIcon, BoxIcon, PlusIcon } from "lucide-react"

import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import { Page, PageHeader } from "@/components/ikas/page"
import { PageNav } from "@/components/ikas/page-nav"

// The ikas panel's own top bar, drawn here only for context. An app never
// renders this: the panel already shows back, logo, app name and the plan button.
function PanelBar() {
  return (
    <div aria-hidden className="flex h-18 items-center gap-4 border-b bg-card px-6">
      <Button variant="outline" size="icon-sm" tabIndex={-1} className="pointer-events-none">
        <ArrowLeftIcon />
      </Button>
      <span className="flex size-10 items-center justify-center rounded-lg bg-foreground text-background">
        <BoxIcon className="size-5" />
      </span>
      <span className="text-lg font-semibold">Uygulama</span>
      <Button color="violet" tabIndex={-1} className="pointer-events-none ml-auto">
        Planı Yönet
      </Button>
    </div>
  )
}

const labels = {
  templates: "Şablonlar",
  shipments: "Gönderimler",
  printers: "Yazıcılar",
  history: "Geçmiş",
} as const

type Section = keyof typeof labels

export default function PageNavInPanel() {
  const [section, setSection] = React.useState<Section>("shipments")

  return (
    <div className="flex w-full flex-col overflow-hidden rounded-xl border bg-background shadow-card">
      <PanelBar />
      <Page width="full" className="pt-0">
        <PageNav
          value={section}
          onValueChange={setSection}
          items={[
            { value: "templates", label: labels.templates },
            { value: "shipments", label: labels.shipments, badge: 3 },
            { value: "printers", label: labels.printers },
            { value: "history", label: labels.history },
          ]}
        />
        <PageHeader
          title={labels[section]}
          description="Başlık bölümün adıdır; uygulamanın adı ve logosu panelin üst barında zaten var."
          actions={
            <Button>
              <PlusIcon data-icon="inline-start" />
              Etiket oluştur
            </Button>
          }
        />
        <Card>
          <CardContent className="text-sm text-muted-foreground">{labels[section]} bölümünün içeriği.</CardContent>
        </Card>
      </Page>
    </div>
  )
}
```

### Linkler

Router kullanan uygulamalar için. `onValueChange` vermezseniz öğeler `href`'li link olur; `linkComponent` ile `next/link` gibi kendi link bileşeninizi verin. Aktif bölümü mevcut route'tan hesaplayıp `value` ile geçin. `disabled` öğe görünür ama tıklanmaz.

**Örnek: Linkler** (`page-nav/links`) · [Canlı önizleme](https://builders.ikas.com/ui-preview/demo/page-nav/links)

```tsx
import * as React from "react"
import Link from "next/link"
import { useSearchParams } from "next/navigation"

import { Card, CardContent } from "@/components/ui/card"
import { Page, PageHeader } from "@/components/ikas/page"
import { PageNav } from "@/components/ikas/page-nav"

const labels = {
  overview: "Genel bakış",
  shipments: "Gönderimler",
  settings: "Ayarlar",
  reports: "Raporlar",
} as const

type Section = keyof typeof labels

/** Router apps: items are links and the active one comes from the route. Here a query param stands in for a path. */
function Sections() {
  const params = useSearchParams()
  const current = params.get("section")
  const section: Section = current && current in labels ? (current as Section) : "overview"

  return (
    <Page width="full" className="py-0">
      <PageNav
        value={section}
        linkComponent={Link}
        items={[
          { value: "overview", label: labels.overview, href: "?section=overview" },
          { value: "shipments", label: labels.shipments, href: "?section=shipments", badge: 3 },
          { value: "settings", label: labels.settings, href: "?section=settings" },
          { value: "reports", label: labels.reports, href: "?section=reports", disabled: true },
        ]}
      />
      <PageHeader title={labels[section]} />
      <Card>
        <CardContent className="text-sm text-muted-foreground">{labels[section]} bölümünün içeriği.</CardContent>
      </Card>
    </Page>
  )
}

// useSearchParams needs a Suspense boundary on statically exported pages.
export default function PageNavLinks() {
  return (
    <React.Suspense>
      <Sections />
    </React.Suspense>
  )
}
```

### İç içe gezinme

1. seviye alt çizgili `PageNav`, 2. seviye kart başlığında `SegmentedControl` olur. İki hap satırı ya da iki alt çizgili bar iç içe kullanılmaz.

**Örnek: İç içe gezinme** (`page-nav/nested`) · [Canlı önizleme](https://builders.ikas.com/ui-preview/demo/page-nav/nested)

```tsx
import * as React from "react"

import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Page, PageHeader } from "@/components/ikas/page"
import { PageNav } from "@/components/ikas/page-nav"
import { SegmentedControl } from "@/components/ikas/segmented-control"

const data = {
  "7": { value: "₺48.210", label: "son 7 gün" },
  "30": { value: "₺184.320", label: "son 30 gün" },
  "90": { value: "₺512.940", label: "son 90 gün" },
} as const

/** Level 1 is the underlined PageNav, level 2 the pill inside the card. Never two pills. */
export default function PageNavNested() {
  const [section, setSection] = React.useState("overview")
  const [range, setRange] = React.useState<keyof typeof data>("30")

  return (
    <Page width="full" className="py-0">
      <PageNav
        value={section}
        onValueChange={setSection}
        items={[
          { value: "overview", label: "Genel bakış" },
          { value: "orders", label: "Siparişler" },
          { value: "settings", label: "Ayarlar" },
        ]}
      />
      <PageHeader title="Genel bakış" />
      <Card>
        <CardHeader>
          <CardTitle>Ciro</CardTitle>
          <CardAction>
            <SegmentedControl
              size="sm"
              mode="radio"
              aria-label="Zaman aralığı"
              value={range}
              onValueChange={setRange}
              options={[
                { value: "7", label: "7G" },
                { value: "30", label: "30G" },
                { value: "90", label: "90G" },
              ]}
            />
          </CardAction>
        </CardHeader>
        <CardContent className="flex flex-col gap-0.5">
          <span className="text-2xl font-semibold tracking-[-0.02em] tabular-nums">{data[range].value}</span>
          <span className="text-[13px] text-muted-foreground">{data[range].label}</span>
        </CardContent>
      </Card>
    </Page>
  )
}
```

## Kullanım ilkeleri

<Callout type="warning" title="Üçüncü seviye gerekiyorsa">
Gezinme en fazla iki seviyelidir: bölümler için `PageNav`, kart veya araç çubuğu içinde görünüm için `SegmentedControl` ya da `Tabs`. Daha derin bir yapı gerekiyorsa ekranları bölün, `Collapse` veya `Select` kullanın.
</Callout>

**Yapın:**

- En fazla 6 bölüm kullanın; bölüm adları kısa ve tek kelime olsun.
- PageNav'i Page'in ilk çocuğu yapın; altına bölümün adıyla bir PageHeader koyun.
- Router'lı uygulamada aktif bölümü route'tan hesaplayın ve value ile verin.

**Yapmayın:**

- Uygulama logosu, uygulama adı, kendi üst barı veya sidebar çizmeyin; panel bunları zaten gösterir.
- PageHeader başlığına uygulamanın adını yazmayın; başlık bölümün adıdır.
- PageNav'in altına ikinci bir alt çizgili bar ya da iki hap sekme satırını iç içe koymayın.

## Erişilebilirlik

- Sekme modunda bar `role="tablist"`, öğeler `role="tab"` ve `aria-selected` taşır. ← / → ile sonraki ve önceki, Home / End ile ilk ve son bölüme geçilir; `disabled` öğeler atlanır.
- Link modunda bar bir `nav` öğesidir ve aktif link `aria-current="page"` taşır.
- Barın erişilebilir adı varsayılan olarak "Bölümler"dir; `aria-label` ile değiştirebilirsiniz.
- Bar sayfa kaydırılırken üstte sabit kalır; dar ekranlarda alt satıra kaymaz, yatay kayar.
- Sekme modunda `value` hiçbir öğeyle eşleşmezse Tab ile ilk etkin öğeye gelinir; bar klavyeyle her zaman erişilebilir kalır.

## API

### `PageNav`

| Prop | Tip | Varsayılan | Açıklama |
| --- | --- | --- | --- |
| `items` (zorunlu) | `PageNavItem<T>[]` | - | Bölümler, sırasıyla. Altıdan fazla olmasın. Alanlar için aşağıdaki tabloya bakın. |
| `value` (zorunlu) | `T` | - | Aktif bölüm. Link modunda mevcut route'a karşılık gelen değeri verin. |
| `onValueChange` | `(value: T) => void` | - | Verilirse öğeler sekme olur ve bölüm yerinde değişir (tek sayfalık uygulama). Vermezseniz öğeler href'li link olur. |
| `linkComponent` | `React.ElementType` | `"a"` | href'li öğeler için link bileşeni, örn. next/link. |

### `PageNavItem`

| Alan | Tip | Açıklama |
| --- | --- | --- |
| `value` | `string` | Bölümün sabit kimliği. |
| `label` | `ReactNode` | Bölümün adı. |
| `href` | `string` | Link adresi. Yalnızca `onValueChange` verilmediğinde kullanılır. |
| `badge` | `ReactNode` | Etiketten sonra gösterilen sayı, örn. bekleyen iş sayısı. |
| `icon` | `ReactNode` | Etiketin önündeki ikon. |
| `disabled` | `boolean` | Öğe görünür ama seçilemez. |
