Skip to content
Brok UI

Loading…

No results

Pagination

Open source Core

Page navigation with previous/next controls, page links, status text, disabled edges, outline/icon-only styles, a page-size slot, and a go-to-page form (GET, or Alpine state with x-page).

Version
v1.4.2
Stability
stable
License
MIT
Related
Data Table
Livewire Data Table
Table

Preview

Align
Disabled
previews.components.pagination.default.blade.php Blade
<x-ui.pagination>
    <x-ui.pagination.content>
        <x-ui.pagination.item>
            <x-ui.pagination.previous href="#" />
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.link href="#">1</x-ui.pagination.link>
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.link href="#" :active="true">2</x-ui.pagination.link>
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.link href="#">3</x-ui.pagination.link>
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.ellipsis />
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.link href="#">10</x-ui.pagination.link>
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.next href="#" />
        </x-ui.pagination.item>
    </x-ui.pagination.content>
</x-ui.pagination>
Start Current
Center Current
End Current

Installation

terminal
php artisan ui:add pagination

Registry contract

php artisan ui:add pagination writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.

  • blade resources/views/components/ui/pagination.blade.php
  • blade resources/views/components/ui/pagination/content.blade.php
  • blade resources/views/components/ui/pagination/item.blade.php
  • blade resources/views/components/ui/pagination/status.blade.php
  • blade resources/views/components/ui/pagination/link.blade.php
  • blade resources/views/components/ui/pagination/previous.blade.php
  • blade resources/views/components/ui/pagination/next.blade.php
  • blade resources/views/components/ui/pagination/ellipsis.blade.php
  • blade resources/views/components/ui/pagination/jump.blade.php
Registry dependencies
None — installs on its own.
Packages
composer: jml/brok:^0.2

Use with AI

A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.

pagination.md
# Brok UI: Pagination (`pagination`)

Page navigation with previous/next controls, page links, status text, disabled edges, outline/icon-only styles, a page-size slot, and a go-to-page form (GET, or Alpine state with x-page).

Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.

## Install

```bash
php artisan ui:add pagination
```

## Usage

```blade
<x-ui.pagination>
    <x-ui.pagination.content>
        <x-ui.pagination.item>
            <x-ui.pagination.previous href="#" />
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.link href="#">1</x-ui.pagination.link>
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.link href="#" :active="true">2</x-ui.pagination.link>
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.link href="#">3</x-ui.pagination.link>
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.ellipsis />
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.link href="#">10</x-ui.pagination.link>
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.next href="#" />
        </x-ui.pagination.item>
    </x-ui.pagination.content>
</x-ui.pagination>
```

## Props

- `align` (string, default `center`) — Declared by @props in the shipped Blade source.
- `disabled` (false|true, default `false`) — Documented catalog control used by the preview workbench.
- `href` (string, default `#`) — Declared by @props in the registry Blade source.
- `active` (bool, default `false`) — Declared by @props in the registry Blade source.
- `variant` (string, default `ghost`) — Declared by @props in the registry Blade source.
- `showLabel` (bool, default `true`) — Declared by @props in the registry Blade source.
- `label` (mixed|null, default `null`) — Declared by @props in the registry Blade source.
- `action` (mixed|null, default `null`) — Declared by @props in the registry Blade source.
- `name` (string, default `page`) — Declared by @props in the registry Blade source.
- `current` (int, default `1`) — Declared by @props in the registry Blade source.
- `total` (mixed|null, default `null`) — Declared by @props in the registry Blade source.
- `params` (array, default `[]`) — Declared by @props in the registry Blade source.
- `xPage` (mixed|null, default `null`) — Declared by @props in the registry Blade source.
- `xTotal` (mixed|null, default `null`) — Declared by @props in the registry Blade source.
- `buttonLabel` (mixed|null, default `null`) — Declared by @props in the registry Blade source.

## Use when

- Use to orient users and help them move across pages, sections, or commands.
- Navigating a long, server- or client-paginated list or table by page number.
- Needing a page-jump form, either a plain GET request or bound to Alpine/Livewire client state.

## Avoid when

- Do not hide primary wayfinding in novelty interactions or deep nested structures if straightforward navigation would be clearer.
- The list is short enough to show in full, or infinite scroll fits the content better than discrete pages.
- Only a simple prev/next flow is needed with no page numbers; a plain button pair is lighter weight.

## Anti-patterns

- Hiding primary wayfinding in novelty interactions

## Rules

- Use the `<brok:pagination>` tag (or `<x-ui.pagination>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.

## Links

- Docs: https://brokui.dev/docs/components/pagination
- Registry JSON (files, props, contract): https://brokui.dev/r/open/pagination.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

Examples

client-jump.blade.php Blade
{{-- Client mode: `x-page` names the Alpine state; Go (or Enter) clamps the number,
     writes it back and dispatches `pagination:jump`. Nothing is submitted. --}}
<div x-data="{ page: 4, total: 24 }" class="flex w-full flex-col gap-4">
    <x-ui.pagination align="between">
        <x-ui.pagination.status class="px-0" x-text="`Page ${page} of ${total}`"></x-ui.pagination.status>
        <x-ui.pagination.content>
            <x-ui.pagination.item>
                <x-ui.pagination.previous href="#" :show-label="false" @click.prevent="page = Math.max(1, page - 1)" />
            </x-ui.pagination.item>
            <x-ui.pagination.item>
                <x-ui.pagination.next href="#" :show-label="false" @click.prevent="page = Math.min(total, page + 1)" />
            </x-ui.pagination.item>
        </x-ui.pagination.content>
        <x-ui.pagination.jump x-page="page" x-total="total" />
    </x-ui.pagination>
</div>
variants.blade.php Blade
<div class="flex flex-col gap-10">
    {{-- Previous / Next only --}}
    <div class="flex flex-col gap-3">
        <p class="text-sm font-medium text-muted-foreground">{{ __('Previous / next only') }}</p>
        <x-ui.pagination>
            <x-ui.pagination.content>
                <x-ui.pagination.item>
                    <x-ui.pagination.previous href="#" />
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.next href="#" />
                </x-ui.pagination.item>
            </x-ui.pagination.content>
        </x-ui.pagination>
    </div>

    {{-- Compact "Page X of Y" --}}
    <div class="flex flex-col gap-3">
        <p class="text-sm font-medium text-muted-foreground">{{ __('Compact') }}</p>
        <x-ui.pagination>
            <x-ui.pagination.content>
                <x-ui.pagination.item>
                    <x-ui.pagination.previous href="#" :show-label="false" />
                </x-ui.pagination.item>
                <x-ui.pagination.status>{{ __('Page :current of :total', ['current' => 2, 'total' => 12]) }}</x-ui.pagination.status>
                <x-ui.pagination.item>
                    <x-ui.pagination.next href="#" :show-label="false" />
                </x-ui.pagination.item>
            </x-ui.pagination.content>
        </x-ui.pagination>
    </div>

    {{-- Outline buttons --}}
    <div class="flex flex-col gap-3">
        <p class="text-sm font-medium text-muted-foreground">{{ __('Outline buttons') }}</p>
        <x-ui.pagination>
            <x-ui.pagination.content>
                <x-ui.pagination.item>
                    <x-ui.pagination.link href="#" variant="outline">1</x-ui.pagination.link>
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.link href="#" variant="outline" :active="true">2</x-ui.pagination.link>
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.link href="#" variant="outline">3</x-ui.pagination.link>
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.link href="#" variant="outline">4</x-ui.pagination.link>
                </x-ui.pagination.item>
            </x-ui.pagination.content>
        </x-ui.pagination>
    </div>

    {{-- Disabled edges (first page) --}}
    <div class="flex flex-col gap-3">
        <p class="text-sm font-medium text-muted-foreground">{{ __('Disabled edges') }}</p>
        <x-ui.pagination>
            <x-ui.pagination.content>
                <x-ui.pagination.item>
                    <x-ui.pagination.previous :disabled="true" />
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.link href="#" :active="true">1</x-ui.pagination.link>
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.link href="#">2</x-ui.pagination.link>
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.link href="#">3</x-ui.pagination.link>
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.next href="#" />
                </x-ui.pagination.item>
            </x-ui.pagination.content>
        </x-ui.pagination>
    </div>

    {{-- Result count + pager (spread to edges) --}}
    <div class="flex flex-col gap-3">
        <p class="text-sm font-medium text-muted-foreground">{{ __('Results summary') }}</p>
        <x-ui.pagination align="between">
            <p class="text-sm text-muted-foreground">{{ __('Showing :from–:to of :total results', ['from' => 1, 'to' => 10, 'total' => 97]) }}</p>
            <x-ui.pagination.content>
                <x-ui.pagination.item>
                    <x-ui.pagination.previous href="#" />
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.link href="#" :active="true">1</x-ui.pagination.link>
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.link href="#">2</x-ui.pagination.link>
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.link href="#">3</x-ui.pagination.link>
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.next href="#" />
                </x-ui.pagination.item>
            </x-ui.pagination.content>
        </x-ui.pagination>
    </div>

    {{-- Page-size select + status (interactive window) --}}
    <div class="flex flex-col gap-3">
        <p class="text-sm font-medium text-muted-foreground">{{ __('Rows per page') }}</p>
        <x-ui.pagination align="between">
            <div class="flex items-center gap-2">
                <span class="text-sm text-muted-foreground">{{ __('Rows per page') }}</span>
                <x-ui.select size="sm" class="w-20" aria-label="{{ __('Rows per page') }}">
                    <option>10</option>
                    <option>20</option>
                    <option>50</option>
                </x-ui.select>
            </div>
            <x-ui.pagination.content>
                <x-ui.pagination.status>{{ __('Page :current of :total', ['current' => 1, 'total' => 9]) }}</x-ui.pagination.status>
                <x-ui.pagination.item>
                    <x-ui.pagination.previous href="#" :show-label="false" :disabled="true" />
                </x-ui.pagination.item>
                <x-ui.pagination.item>
                    <x-ui.pagination.next href="#" :show-label="false" />
                </x-ui.pagination.item>
            </x-ui.pagination.content>
        </x-ui.pagination>
    </div>
</div>
with-jump.blade.php Blade
{{-- Status on the start edge, the pager in the middle, a go-to-page form on the end edge. --}}
<x-ui.pagination align="between">
    <x-ui.pagination.status class="px-0">{{ __('Page :current of :total', ['current' => 4, 'total' => 24]) }}</x-ui.pagination.status>
    <x-ui.pagination.content>
        <x-ui.pagination.item>
            <x-ui.pagination.previous href="#" :show-label="false" />
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.link href="#">3</x-ui.pagination.link>
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.link href="#" :active="true">4</x-ui.pagination.link>
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.link href="#">5</x-ui.pagination.link>
        </x-ui.pagination.item>
        <x-ui.pagination.item>
            <x-ui.pagination.next href="#" :show-label="false" />
        </x-ui.pagination.item>
    </x-ui.pagination.content>
    <x-ui.pagination.jump :current="4" :total="24" :params="['sort' => 'newest']" />
</x-ui.pagination>

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
align string center Declared by @props in the shipped Blade source.
disabled false | true false Documented catalog control used by the preview workbench.
href string # Declared by @props in the registry Blade source.
active bool false Declared by @props in the registry Blade source.
variant string ghost Declared by @props in the registry Blade source.
showLabel bool true Declared by @props in the registry Blade source.
label mixed | null null Declared by @props in the registry Blade source.
action mixed | null null Declared by @props in the registry Blade source.
name string page Declared by @props in the registry Blade source.
current int 1 Declared by @props in the registry Blade source.
total mixed | null null Declared by @props in the registry Blade source.
params array [] Declared by @props in the registry Blade source.
xPage mixed | null null Declared by @props in the registry Blade source.
xTotal mixed | null null Declared by @props in the registry Blade source.
buttonLabel mixed | null null Declared by @props in the registry Blade source.

Slots

  • default — Compositional parts: content, links, previous/next, ellipsis, status, and jump.
  • x-ui.pagination.content — Installed subcomponent from the registry item.
  • x-ui.pagination.item — Installed subcomponent from the registry item.
  • x-ui.pagination.status — Installed subcomponent from the registry item.
  • x-ui.pagination.link — Installed subcomponent from the registry item.
  • x-ui.pagination.previous — Installed subcomponent from the registry item.
  • x-ui.pagination.next — Installed subcomponent from the registry item.
  • x-ui.pagination.ellipsis — Installed subcomponent from the registry item.
  • x-ui.pagination.jump — Installed subcomponent from the registry item.

Data slots

Stable hooks for CSS overrides and browser tests.

pagination pagination-content pagination-ellipsis pagination-item pagination-jump pagination-link pagination-next pagination-previous pagination-status

Behavior

  • In server mode, pagination.jump renders a plain GET form so it works without JavaScript and with any server-side paginator.
  • In client mode (xPage set), the same form never submits; typing a number and pressing Go or Enter clamps it to the valid range, writes it back to the Alpine expression, and dispatches a pagination:jump event.
  • Previous/next and page links become disabled or drop out at the edges of the page range rather than linking to an invalid page.
  • Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.

Guidance

Navigation and orientation

Orient users and move between destinations.

Use when

  • Use to orient users and help them move across pages, sections, or commands.
  • Navigating a long, server- or client-paginated list or table by page number.
  • Needing a page-jump form, either a plain GET request or bound to Alpine/Livewire client state.

Avoid when

  • Do not hide primary wayfinding in novelty interactions or deep nested structures if straightforward navigation would be clearer.
  • The list is short enough to show in full, or infinite scroll fits the content better than discrete pages.
  • Only a simple prev/next flow is needed with no page numbers; a plain button pair is lighter weight.

Use instead

  • Visible links and local navigation

Anti-patterns

  • Hiding primary wayfinding in novelty interactions
Anatomy
root pagination-content pagination-ellipsis pagination-item pagination-jump pagination-link pagination-next pagination-previous pagination-status
Theming hooks
pagination pagination-content pagination-ellipsis pagination-item pagination-jump pagination-link pagination-next pagination-previous pagination-status

Accessibility

WCAG 2.2 AA Keyboard focus-visible RTL-ready Localized labels Dark mode
Keyboard
native
Focus
native
  • The current page link is marked with aria-current="page" so assistive tech identifies it without relying on styling alone.
  • The jump input carries a real associated label, so its purpose is announced even though it is a single number field.
  • Semantic HTML and a stable data-slot attribute for styling and scripting hooks.
  • Focus-visible rings use the ring token, so keyboard focus is always visible.
  • Disabled and invalid states are conveyed to assistive tech, not by color alone.
  • Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
  • Labels go through __() and layout uses logical properties (ms-*, text-start), so it mirrors under dir="rtl" — flip the preview to RTL to confirm.
  • Dark mode uses the same semantic tokens under the dark class; high contrast follows forced-color system tokens.

Livewire

Needs wire:key

Add a stable wire:key when Livewire can reorder this interactive component.

livewire-component.blade.php Blade
<div wire:key="pagination-{{ $record->id }}">
    <x-ui.pagination>
        <x-ui.pagination.content>
            <x-ui.pagination.item>
                <x-ui.pagination.previous href="#" />
            </x-ui.pagination.item>
            <x-ui.pagination.item>
                <x-ui.pagination.link href="#">1</x-ui.pagination.link>
            </x-ui.pagination.item>
            <x-ui.pagination.item>
                <x-ui.pagination.link href="#" :active="true">2</x-ui.pagination.link>
            </x-ui.pagination.item>
            <x-ui.pagination.item>
                <x-ui.pagination.link href="#">3</x-ui.pagination.link>
            </x-ui.pagination.item>
            <x-ui.pagination.item>
                <x-ui.pagination.ellipsis />
            </x-ui.pagination.item>
            <x-ui.pagination.item>
                <x-ui.pagination.link href="#">10</x-ui.pagination.link>
            </x-ui.pagination.item>
            <x-ui.pagination.item>
                <x-ui.pagination.next href="#" />
            </x-ui.pagination.item>
        </x-ui.pagination.content>
    </x-ui.pagination>
</div>

Validation

Validation support: native. Keep the error message connected with aria-describedby.

livewire-form.blade.php Blade
<form wire:submit="save" class="space-y-2">
    <brok:pagination
        wire:model="value"
        :aria-invalid="$errors->has('value') ? 'true' : 'false'"
        aria-describedby="value-error"
    />

    @error('value')
        <p id="value-error" role="alert">{{ $message }}</p>
    @enderror

    <brok:button type="submit" wire:loading.attr="disabled">
        <span wire:loading.remove>Save</span>
        <span wire:loading>Saving…</span>
    </brok:button>
</form>

Source

The exact, editable files ui:add writes into your app. Previews render this same code; there are no preview-only components.

resources/views/components/ui/pagination.blade.php Blade
@props([
    // `center` (default) keeps the classic centered pager; `between` spreads
    // children to the edges for status-text / page-size-select layouts.
    'align' => 'center',
])

@php
    $justify = $align === 'between' ? 'justify-between' : 'justify-center';
@endphp

<nav role="navigation" aria-label="{{ __('Pagination navigation') }}" data-slot="pagination" data-align="{{ $align }}" {{ $attributes->merge(['class' => 'mx-auto flex w-full flex-wrap items-center gap-4 '.$justify]) }}>{{ $slot }}</nav>
resources/views/components/ui/pagination/content.blade.php Blade
<ul data-slot="pagination-content" {{ $attributes->merge(['class' => 'flex flex-row items-center gap-1']) }}>{{ $slot }}</ul>
resources/views/components/ui/pagination/item.blade.php Blade
<li data-slot="pagination-item" {{ $attributes }}>{{ $slot }}</li>
resources/views/components/ui/pagination/status.blade.php Blade
<li role="presentation" data-slot="pagination-status" {{ $attributes->merge(['class' => 'flex h-9 items-center px-3 text-sm text-muted-foreground']) }}>{{ $slot }}</li>
resources/views/components/ui/pagination/link.blade.php Blade
@props([
    'href' => '#',
    'active' => false,
    'disabled' => false,
    'variant' => 'ghost',
])

@php
    $styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
    $variant = $styles['normalizeVariant']($variant);
    // `ghost` (default) keeps the original look; `outline` gives every page a
    // visible border (Flexnative "outline buttons"). The active page always
    // reads as selected regardless of variant.
    $base = 'inline-flex h-9 min-w-9 items-center justify-center gap-1 rounded-md border px-3 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none';

    if ($active) {
        $state = 'border-border bg-accent text-accent-foreground';
    } elseif ($variant === 'outline') {
        $state = 'border-input bg-background text-foreground hover:bg-muted';
    } else {
        $state = 'border-transparent text-foreground hover:bg-muted';
    }

    $disabledClasses = $disabled ? ' pointer-events-none opacity-50' : '';

    $classes = trim($base.' '.$state.$disabledClasses);
@endphp

@if ($disabled)
    {{-- A disabled page is non-navigable: render a span, not an anchor, and
         expose the state to assistive tech. --}}
    <span role="link" data-slot="pagination-link" data-variant="{{ $variant }}" data-active="false" aria-disabled="true" {{ $attributes->merge(['class' => $classes]) }}>{{ $slot }}</span>
@else
    <a href="{{ $href }}" data-slot="pagination-link" data-variant="{{ $variant }}" data-active="{{ $active ? 'true' : 'false' }}" @if($active) aria-current="page" @endif {{ $attributes->merge(['class' => $classes]) }}>{{ $slot }}</a>
@endif
resources/views/components/ui/pagination/previous.blade.php Blade
@props([
    'href' => '#',
    'disabled' => false,
    'showLabel' => true,
    'label' => null,
])

@php
    $text = $label ?? __('Previous');
    // Icon-only mode keeps a square tap target and an accessible name; the
    // visible word collapses to an sr-only label.
    $padding = $showLabel ? 'ps-2 pe-3' : 'size-9 justify-center';
    $base = 'inline-flex h-9 items-center justify-center gap-1 rounded-md text-sm font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none';
    $classes = trim($base.' '.$padding.($disabled ? ' pointer-events-none opacity-50' : ''));
@endphp

@if ($disabled)
    <span role="link" data-slot="pagination-previous" aria-disabled="true" aria-label="{{ $text }}" {{ $attributes->merge(['class' => $classes]) }}>
        <svg class="size-4 rtl:-scale-x-100" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="m15 18-6-6 6-6"></path>
        </svg>
        @if ($showLabel)<span>{{ $text }}</span>@else<span class="sr-only">{{ $text }}</span>@endif
    </span>
@else
    <a href="{{ $href }}" data-slot="pagination-previous" aria-label="{{ $text }}" {{ $attributes->merge(['class' => $classes]) }}>
        <svg class="size-4 rtl:-scale-x-100" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="m15 18-6-6 6-6"></path>
        </svg>
        @if ($showLabel)<span>{{ $text }}</span>@else<span class="sr-only">{{ $text }}</span>@endif
    </a>
@endif
resources/views/components/ui/pagination/next.blade.php Blade
@props([
    'href' => '#',
    'disabled' => false,
    'showLabel' => true,
    'label' => null,
])

@php
    $text = $label ?? __('Next');
    // Icon-only mode keeps a square tap target and an accessible name; the
    // visible word collapses to an sr-only label.
    $padding = $showLabel ? 'ps-3 pe-2' : 'size-9 justify-center';
    $base = 'inline-flex h-9 items-center justify-center gap-1 rounded-md text-sm font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none';
    $classes = trim($base.' '.$padding.($disabled ? ' pointer-events-none opacity-50' : ''));
@endphp

@if ($disabled)
    <span role="link" data-slot="pagination-next" aria-disabled="true" aria-label="{{ $text }}" {{ $attributes->merge(['class' => $classes]) }}>
        @if ($showLabel)<span>{{ $text }}</span>@else<span class="sr-only">{{ $text }}</span>@endif
        <svg class="size-4 rtl:-scale-x-100" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="m9 18 6-6-6-6"></path>
        </svg>
    </span>
@else
    <a href="{{ $href }}" data-slot="pagination-next" aria-label="{{ $text }}" {{ $attributes->merge(['class' => $classes]) }}>
        @if ($showLabel)<span>{{ $text }}</span>@else<span class="sr-only">{{ $text }}</span>@endif
        <svg class="size-4 rtl:-scale-x-100" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="m9 18 6-6-6-6"></path>
        </svg>
    </a>
@endif
resources/views/components/ui/pagination/ellipsis.blade.php Blade
<span data-slot="pagination-ellipsis" aria-hidden="true" {{ $attributes->merge(['class' => 'flex size-9 items-center justify-center']) }}>
    <svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
        <circle cx="12" cy="12" r="1"></circle>
        <circle cx="19" cy="12" r="1"></circle>
        <circle cx="5" cy="12" r="1"></circle>
    </svg>
    <span class="sr-only">{{ __('More pages') }}</span>
</span>
resources/views/components/ui/pagination/jump.blade.php Blade
@props([
    // Where the form submits (GET). Defaults to the current URL, so `?page=N`
    // lands on Laravel's paginator without extra wiring.
    'action' => null,
    // Query key for the page number.
    'name' => 'page',
    'current' => 1,
    // Last page; leave null when unknown (the input then has no max).
    'total' => null,
    // Extra query params carried along (filters, sort) as hidden inputs.
    'params' => [],
    // Client mode: an Alpine expression holding the page number (`page`,
    // `$wire.page`, `table.page`). The form then never submits: Go (or Enter)
    // clamps the typed number, writes it back and dispatches
    // `pagination:jump` { page }. `xTotal` is the matching last-page expression.
    'xPage' => null,
    'xTotal' => null,
    'label' => null,
    'buttonLabel' => null,
])

@php
    $text = $label ?? __('Go to page');
    $button = $buttonLabel ?? __('Go');
    $id = 'pagination-jump-'.str()->random(6);
    $client = $xPage !== null;
    $maxExpr = $client ? ($xTotal ?? ($total !== null ? (int) $total : 'Infinity')) : null;
    $field = 'inline-flex h-9 w-16 rounded-md border border-input bg-background px-3 text-center text-sm text-foreground shadow-xs transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-[length:var(--ring-width)] focus-visible:ring-ring focus-visible:ring-offset-[length:var(--ring-offset-width)] focus-visible:ring-offset-background motion-reduce:transition-none';
    $submit = 'inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium text-foreground shadow-xs transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-[length:var(--ring-width)] focus-visible:ring-ring focus-visible:ring-offset-[length:var(--ring-offset-width)] focus-visible:ring-offset-background motion-reduce:transition-none';
@endphp

{{-- Type a page number and go. Server mode is a plain GET form, so it works
     without JS and with any server-side paginator; client mode (`x-page`)
     keeps the same markup and writes the Alpine state instead. Composes with
     `align="between"`. --}}
<form
    method="get"
    @if ($action !== null) action="{{ $action }}" @endif
    data-slot="pagination-jump"
    data-mode="{{ $client ? 'client' : 'server' }}"
    @if ($client)
        novalidate
        x-data="{ jumpDraft: '' }"
        x-effect="jumpDraft = String({{ $xPage }})"
        @submit.prevent="const max = Number({{ $maxExpr }}) || Infinity; const n = Math.min(Math.max(1, parseInt(jumpDraft, 10) || 1), max); {{ $xPage }} = n; jumpDraft = String(n); $dispatch('pagination:jump', { page: n })"
    @endif
    {{ $attributes->merge(['class' => 'flex items-center gap-2']) }}
>
    @unless ($client)
        @foreach ($params as $key => $value)
            @if ($key !== $name)
                <input type="hidden" name="{{ $key }}" value="{{ $value }}" />
            @endif
        @endforeach
    @endunless
    <label for="{{ $id }}" class="text-sm text-muted-foreground">{{ $text }}</label>
    <input
        id="{{ $id }}"
        type="number"
        name="{{ $name }}"
        inputmode="numeric"
        min="1"
        @if ($client)
            x-model="jumpDraft"
            :max="{{ $maxExpr }} === Infinity ? null : {{ $maxExpr }}"
        @else
            @if ($total !== null) max="{{ (int) $total }}" @endif
            value="{{ (int) $current }}"
        @endif
        required
        class="{{ $field }}"
    />
    @if ($client && $xTotal !== null)
        <span class="text-sm text-muted-foreground" x-text="`{{ __('of :total', ['total' => '${'.$xTotal.'}']) }}`"></span>
    @elseif ($total !== null)
        <span class="text-sm text-muted-foreground">{{ __('of :total', ['total' => (int) $total]) }}</span>
    @endif
    <button type="submit" class="{{ $submit }}">{{ $button }}</button>
</form>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.4.2
Status
Stable
License
open
Accessibility reviewed
No review date recorded
Last breaking change
No date recorded
Deprecation
Not deprecated
Contract
v6
Foundation
≥ 1.0.0