Pagination
Page navigation control. Numbered, prev/next-only, and compact variants.
Overview
The Pagination component is a core building block of the Byteion design system, providing a robust, accessibility-compliant interface for its specific UI capabilities.
Why this exists
Pagination enables navigation through large datasets. It communicates the total volume of data and the user's current position within it.
Mental model
Pagination splits a large list into manageable pages. It shows page numbers, prev/next, and optionally total count and per-page selector.
When to use
When not to use
Basic example
The following example demonstrates standard usage, fully compliant with Byteion's token-first approach.
1. Numbered pagination
Default pagination with page numbers.
2. Numbered with ellipsis
Shows ellipsis when there are many pages.
3. Previous / Next
Simple pagination with only previous and next controls.
4. Compact pagination
Compact pagination for tight spaces.
5. First / Previous / Next / Last
Full set of navigation controls.
6. Disabled states
Controls are disabled on first and last pages.
On first page
On last page
7. Datatable pagination (with info)
Used with data tables to show record information.
8. Mobile pagination
Optimized pagination for mobile screens.
Mobile - Compact
Simple and minimal.
Mobile - With page numbers
Shows a few pages with ellipsis.
Mobile - Previous / Next
Best for very small screens.
Best practice: Use numbered pagination for most cases. Use compact or previous/next on mobile screens.
import { useState } from 'react';
import { Pagination } from '@byteion/react';
export default function PaginationDemo() {
const [page, setPage] = useState(3);
return (
<div className="space-y-6 p-6">
<Pagination page={page} totalPages={10} onPageChange={setPage} />
<Pagination page={1} totalPages={5} onPageChange={() => {}} variant="compact" />
</div>
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { Pagination } from '@byteion/vue';
const page = ref(3);
</script>
<template>
<div class="space-y-6 p-6">
<Pagination v-model="page" :total-pages="10" />
<Pagination :model-value="1" :total-pages="5" variant="compact" />
</div>
</template>
Variants
Each variant communicates a different meaning. Choose based on priority, not aesthetics.
Single default variant.
Sizes
Size should match the importance and context of the component.
- md — md size.
States
This component reacts seamlessly to the following interaction and data states:
- default — Resting appearance before interaction.
- active — Currently selected or in use.
- disabled — Non-interactive — grayed out, cursor not-allowed.
- hover — Pointer is over the element — visual feedback via color or shadow.
- focus — Keyboard or tab focus — shows ring indicator for accessibility.
Anatomy
Usage guidelines
- Show 5-7 page numbers at most.
- Always show prev/next.
- Include a per-page selector for large datasets.
Do and Don't
Do
- Show prev/next always.
- Limit visible page numbers.
- Include total count.
Don't
- Do not show all page numbers for large datasets.
- Do not disable prev/next when other pages exist.
- Do not omit the per-page selector for 100+ items.
Accessibility
Uses <nav aria-label="Pagination">. Current page has aria-current="page". Prev/Next have descriptive labels.
Role:navigation
Customization
Active page color follows --primary.
Compose with React cva()
Use the exportedcva() +cn() utilities to compose your own variant on top of the Byteion tokens.
import { cva, cn, type VariantProps } from '@byteion/react';
const myPagination = cva('base classes…', {
variants: {
accent: { soft:'border-primary-200', loud:'border-primary-500' },
},
defaultVariants: { accent:'soft' },
});
type Props = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof myPagination>;
Compose with Vue cva()
Use the exportedcva() +cn() utilities inside standard Vue setup blocks.
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { cva, type VariantProps } from '@byteion/vue';
interface Props { accent?:'soft' |'loud' }
const props = withDefaults(defineProps<Props>(), { accent:'soft' });
const attrs = useAttrs();
const myPagination = cva('base classes…', {
variants: { accent: { soft:'border-primary-200', loud:'border-primary-500' } },
});
const cls = computed(() => myPagination({ accent: props.accent, class: attrs.class as string | undefined }));
</script>
Runtime events
Byteion keeps framework APIs idiomatic: HTML uses DOM custom events, React uses callback props, and Vue uses emitted events / v-model updates.
HTML / JS events
No HTML runtime events are declared by this adapter.
React callbacks
onPageChange(page: number) => void
Vue emits
pageChange[page: number]
Remote / AJAX
This component does not implement Byteion JS remote loading. Use normal HTML form submission, framework state, or your application data layer around it.
API Reference
Static HTML. Page changes trigger navigation or API call.
HTML Data Attributes
No custom data attributes required.
React Props · PaginationProps
Extracted directly fromreact/src/components/Pagination.tsx.
export interface PaginationProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {
/** Current 1-based page number. */
page: number;
/** Total number of pages. */
totalPages: number;
/** Called with the new page number. */
onPageChange: (page: number) => void;
/** Visual layout. */
variant?: 'numbered' | 'prev-next' | 'compact';
/** Max number of page buttons shown in the numbered variant. Default 5. */
siblings?: number;
/** Accessible label for the nav element. */
'aria-label'?: string;
}
Vue Props · PaginationProps
Extracted directly fromvue/src/components/Pagination.vue.
export interface PaginationProps {
/** Current 1-based page number. */
page: number;
/** Total number of pages. */
totalPages: number;
/** Visual layout. */
variant?: 'numbered' | 'prev-next' | 'compact';
/** Max number of page buttons shown in the numbered variant. Default 5. */
siblings?: number;
/** Accessible label for the nav element. */
ariaLabel?: string;
}
Developer tips
Common mistakes
- Showing too many page buttons — use the
siblingsprop to limit the range. - Losing filter/search/sort query when the page changes — preserve state in the parent component or URL.
- Using
<button>for server-side links that should be crawlable or deep-linkable — use<a>tags instead. - Using
<a>for purely client-side state without preventing default navigation. - Forgetting disabled states on the first and last page — the component handles this automatically.
- Not resetting to page 1 after changing filters or search terms.
- Not announcing the current page to assistive technology —
aria-current="page"is built in. - Using
v-modelwith the Vue Pagination — usepage+@page-changeinstead.
Production usage tips
- For Laravel/server-rendered apps, use anchor links with query params (
?page=2) instead of client-side state. - For React/Vue SPAs, keep page in component state or router query (
useSearchParams/vue-router query). - For data tables, coordinate pagination with sorting, filtering, loading, empty, and error states — a single query state object helps.
- Test with page 1, a middle page, the last page,
totalPages=1, and very large totals (100+ pages). - Test dark mode and branded themes — token-based colors adapt automatically.
- Test disabled state on Previous when on page 1 and Next when on the last page.
Premium examples
Advanced patterns available in Byteion Pro:
Related components
Unlock Premium Patterns
Get access to copy-paste ready Pro blocks, SaaS templates, and advanced data-tables built with this component.
Explore Pro Components