Navigation

Prev Next Counter

The most modest pagination: two links and a counter between them. The numbers are deliberately gone — a feed read in order does not need them.

  • pagination
  • prev-next
  • links
  • minimal

Preview

1440pxHost theme

Use it with AI

  1. 1. Copy the link.
  2. 2. Write to your agent in your own words and drop the link into the sentence.
  3. 3. The agent opens the link and installs the component from the registry.

put this in the header: https://vibeui.ru/c/pagination-004?page=2&lang=en

Show the full instructions

What the link says, spelled out. Use it when your agent cannot open links — paste this text instead.

# Install and place "pagination-004" (Prev Next Counter) from VibeUI

## 1. Install first — do not skip, do not recreate
Run this exact command before writing any code:
npx shadcn@latest add https://vibeui.ru/r/pagination-004.json

Registry item: https://vibeui.ru/r/pagination-004.json
Installs to: components/vibeui/pagination-004.tsx (the exact path follows this project's components.json aliases).
npm dependencies: none.
Registry dependencies: none.

Install it from the registry. Do not recreate it from the description,
do not substitute a similar component from another library, and do not
rewrite it to match the project's existing style.

## 2. What it is
The most modest pagination: two links and a counter between them. The numbers are deliberately gone — a feed read in order does not need them.

Pagination made of two links and a counter in the middle: "Page 4 of 12". The edges dim instead of disappearing, so the row keeps its width between pages. Zero dependencies, one file, no client JS.

## 3. How to use it
import { Pagination004 } from "@/components/vibeui/pagination-004"

<Pagination004 page={4} total={12} hrefOf={(page) => `/blog?page=${page}`} />

Read the installed file for the full prop list.

## 4. Where to place it
This is a small inline component. Put it exactly where the user asked,
inside the existing markup. Do not create a new page, section or
wrapper for it. If a similar control already sits in that spot,
replace it instead of adding a second one.

Placement: ___
(The user fills this line in. If it is still blank, ask where to put it
instead of guessing.)

## 5. Keep exactly as installed
- the local --vibeui-pagination-004-* palette — do not swap it for your theme tokens (bg-secondary, border-input and the like)
- links rather than buttons: opening in a new tab, copying the address and going back through history all come free
- rel="prev" and rel="next" on the edges: they tell the browser and the crawler how the pages are ordered
- dimming the edge with aria-disabled instead of removing the link — a vanished link shifts the counter
- tabular figures in the counter: otherwise the edge links jitter as the number changes
- the counter as a sentence rather than a bare number: "4 of 12" answers "how much is left", a lone 4 does not

## 6. You may change
- page and total — the current page and the count
- hrefOf — how to build a page address for your router
- label — the word before the number
- the accent through the accent prop — it colours the focus rings

## 7. Rules
- There is no way to jump to an arbitrary page: that is a deliberate omission, use the numbered variant for jumps.
- pointer-events:none at the edge does not block keyboard activation — the href is already absent there, and that is the real guard.
- The total has to be known: for shifting data cursor pagination fits better.
- Do not lift the CSS variables into globals.css: the component has to stay a single file.

## 8. Verify
- it renders with no console errors;
- it looks like the preview on the VibeUI page you copied this from;
- if it does not, you changed something listed in section 5 — put it back.

For developers

npx shadcn@latest add https://vibeui.ru/r/pagination-004.json
https://vibeui.ru/r/pagination-004.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-pagination-004-*. Серверный: хуков нет, границы считаются из пропсов page и total. Адрес соседней страницы собирает функция hrefOf, по умолчанию `?page=N`. Край списка помечен aria-disabled и гасится, а не исчезает, поэтому ряд не прыгает. Счётчик занимает всё свободное место и набран табличными цифрами, стрелки нарисованы бордюрами и поворачиваются по data-dir.

Component source

The same file your agent installs. Here in case you would rather copy it by hand.

import type { CSSProperties } from "react"

export type Pagination004Props = {
  page?: number
  total?: number
  label?: string
  hrefOf?: (page: number) => string
  accent?: string
  className?: string
  style?: CSSProperties
}

// Идея компонента: самая скромная пагинация — две ссылки и счётчик между ними.
// Номера страниц нужны, когда по ним прыгают; в ленте, которую читают подряд,
// они только шумят. Счётчик набран табличными цифрами и держит фиксированную
// ширину, поэтому ссылки по краям не дёргаются при переходе.
const STYLES = `
:where([data-vibeui-block="pagination-004"]){
--vibeui-pagination-004-bg:oklch(1 0 0);
--vibeui-pagination-004-fg:oklch(0.24 0.014 265);
--vibeui-pagination-004-muted:oklch(0.55 0.014 265);
--vibeui-pagination-004-border:oklch(0.91 0.006 265);
--vibeui-pagination-004-hover:oklch(0.55 0.02 265 / 8%);
--vibeui-pagination-004-accent:oklch(0.55 0.2 262);
--vibeui-pagination-004-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="pagination-004"]{
box-sizing:border-box;width:100%;max-width:28rem;padding:0.375rem;
display:flex;align-items:center;gap:0.375rem;
background:var(--vibeui-pagination-004-bg);color:var(--vibeui-pagination-004-fg);
border:1px solid var(--vibeui-pagination-004-border);border-radius:0.75rem;
font-family:var(--vibeui-pagination-004-font);
}
[data-vibeui-block="pagination-004"] [data-part="step"]{
display:inline-flex;align-items:center;gap:0.4375rem;
height:2.25rem;padding:0 0.875rem;border-radius:0.625rem;
text-decoration:none;color:inherit;font-size:0.875rem;
border:1px solid var(--vibeui-pagination-004-border);
transition:background-color .14s ease;
}
[data-vibeui-block="pagination-004"] [data-part="step"]:hover{background:var(--vibeui-pagination-004-hover)}
[data-vibeui-block="pagination-004"] [data-part="step"]:focus-visible{outline:2px solid var(--vibeui-pagination-004-accent);outline-offset:2px}
/* Край списка гасится, а не исчезает: пропавшая ссылка сдвинула бы счётчик. */
[data-vibeui-block="pagination-004"] [data-part="step"][aria-disabled="true"]{
color:var(--vibeui-pagination-004-muted);pointer-events:none;opacity:.55;
}
[data-vibeui-block="pagination-004"] [data-part="arrow"]{
width:0.4375rem;height:0.4375rem;
border:1.5px solid currentColor;border-right:0;border-bottom:0;
transform:rotate(-45deg);
}
[data-vibeui-block="pagination-004"] [data-part="step"][data-dir="next"] [data-part="arrow"]{transform:rotate(135deg)}
[data-vibeui-block="pagination-004"] [data-part="count"]{
flex:1;text-align:center;font-size:0.8125rem;color:var(--vibeui-pagination-004-muted);
font-variant-numeric:tabular-nums;
}
[data-vibeui-block="pagination-004"] [data-part="now"]{color:var(--vibeui-pagination-004-fg);font-weight:650}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="pagination-004"] *{animation:none!important;transition:none!important}}
`

/**
 * Пагинация «назад — счётчик — вперёд» без номеров страниц.
 * Один файл, ноль зависимостей, собственная палитра, клиентского JS нет.
 */
export function Pagination004({
  page = 4,
  total = 12,
  label = "Страница",
  hrefOf = (value: number) => `?page=${value}`,
  accent,
  className,
  style,
}: Pagination004Props) {
  const palette = {
    ...(accent ? { "--vibeui-pagination-004-accent": accent } : null),
    ...style,
  } as CSSProperties

  const first = page <= 1
  const last = page >= total

  return (
    <>
      <style href="vibeui-pagination-004" precedence="medium">
        {STYLES}
      </style>
      <nav
        data-vibeui-block="pagination-004"
        aria-label="Навигация по страницам"
        className={className}
        style={palette}
      >
        <a
          data-part="step"
          data-dir="prev"
          href={first ? undefined : hrefOf(page - 1)}
          aria-disabled={first || undefined}
          rel="prev"
        >
          <span data-part="arrow" aria-hidden="true" />
          Назад
        </a>
        <p data-part="count" aria-current="page">
          {label} <span data-part="now">{page}</span> из {total}
        </p>
        <a
          data-part="step"
          data-dir="next"
          href={last ? undefined : hrefOf(page + 1)}
          aria-disabled={last || undefined}
          rel="next"
        >
          Вперёд
          <span data-part="arrow" aria-hidden="true" />
        </a>
      </nav>
    </>
  )
}