View as Markdown

Pagination

Indicate that related content is split across multiple pages with accessible pagination links.

100%
Loading...
<twig:Pagination ariaLabel="Article pages">
    <twig:Pagination:Item label="Previous" disabled />
    <twig:Pagination:Item href="?page=1" label="1" active />
    <twig:Pagination:Item href="?page=2" label="2" />
    <twig:Pagination:Item href="?page=3" label="3" />
    <twig:Pagination:Item href="?page=2" label="Next" />
</twig:Pagination>

Installation

php bin/console ux:install pagination --kit bootstrap

Install the following Composer dependencies:

composer require twig/extra-bundle twig/html-extra:^3.12.0

Install the following front-end dependencies:

npm install bootstrap@^5.3.0
# ...or with Symfony AssetMapper
php bin/console importmap:require bootstrap bootstrap/dist/css/bootstrap.min.css

Copy the following file(s) into your app:

templates/components/Pagination.html.twig
{# @prop size 'sm'|'lg'|null The pagination control size. #}
{# @prop align 'start'|'center'|'end'|null The horizontal alignment of the pagination links. #}
{# @prop ariaLabel string The accessible name of the navigation landmark. #}
{# @block content The pagination items, typically `Pagination:Item` components. #}
{%- props
    size = null,
    align = null,
    ariaLabel = 'Pagination'
-%}

{%- set pagination_classes = html_classes('pagination', {
    ('pagination-' ~ size): size in ['sm', 'lg'],
    ('justify-content-' ~ align): align in ['start', 'center', 'end'],
}) -%}

<nav {{ attributes.defaults({'aria-label': ariaLabel}) }}>
    <ul class="{{ pagination_classes }}">
        {%- block content -%}{% endblock -%}
    </ul>
</nav>
templates/components/Pagination/Item.html.twig
{# @prop active boolean Whether the item represents the current page. #}
{# @prop disabled boolean Whether the item is unavailable for interaction. #}
{# @prop href string|null The destination of the page link. #}
{# @prop label string The fallback item label when no content block is provided. #}
{# @prop ariaLabel string|null The accessible label for the page link. #}
{# @block content The page link content. #}
{%- props
    active = false,
    disabled = false,
    href = null,
    label = '',
    ariaLabel = null
-%}

{%- set item_classes = html_classes('page-item', {
    active: active,
    disabled: disabled,
}) -%}
<li {{ attributes.defaults({class: item_classes}) }}>
    {%- if href is not null and not disabled -%}
        <a class="page-link" href="{{ href }}"{% if ariaLabel is not null %} aria-label="{{ ariaLabel }}"{% endif %}{% if active %} aria-current="page"{% endif %}>
            {%- block content -%}{{- label -}}{%- endblock -%}
        </a>
    {%- else -%}
        <span class="page-link"{% if ariaLabel is not null %} aria-label="{{ ariaLabel }}"{% endif %}{% if active %} aria-current="page"{% endif %}{% if disabled %} aria-disabled="true"{% endif %}>
            {{- block('content') -}}
        </span>
    {%- endif -%}
</li>

Usage

<twig:Pagination ariaLabel="Search results pages">
    <twig:Pagination:Item href="?page=1" label="1" active />
    <twig:Pagination:Item href="?page=2" label="2" />
    <twig:Pagination:Item href="?page=3" label="3" />
</twig:Pagination>

Accessibility

Give each pagination landmark a descriptive ariaLabel, especially when a page contains more than one pagination control.

The active item exposes aria-current="page". Disabled items render as non-interactive span elements instead of links.

Examples

Overview

Use connected page links to navigate through a series of related pages.

100%
Loading...
<twig:Pagination ariaLabel="Page navigation example">
    <twig:Pagination:Item href="#" label="Previous" />
    <twig:Pagination:Item href="#" label="1" />
    <twig:Pagination:Item href="#" label="2" />
    <twig:Pagination:Item href="#" label="3" />
    <twig:Pagination:Item href="#" label="Next" />
</twig:Pagination>

Working with icons

When replacing previous and next labels with symbols, provide an accessible label and hide the symbol from assistive technologies.

100%
Loading...
<twig:Pagination ariaLabel="Page navigation example">
    <twig:Pagination:Item href="#" ariaLabel="Previous"><span aria-hidden="true">&laquo;</span></twig:Pagination:Item>
    <twig:Pagination:Item href="#" label="1" />
    <twig:Pagination:Item href="#" label="2" />
    <twig:Pagination:Item href="#" label="3" />
    <twig:Pagination:Item href="#" ariaLabel="Next"><span aria-hidden="true">&raquo;</span></twig:Pagination:Item>
</twig:Pagination>

Active

Mark the page currently being viewed as active. It can remain a link or render as a non-interactive item.

100%
Loading...
<div>
    <twig:Pagination ariaLabel="Article pages">
        <twig:Pagination:Item href="#" label="Previous" />
        <twig:Pagination:Item href="#" label="1" />
        <twig:Pagination:Item href="#" label="2" active />
        <twig:Pagination:Item href="#" label="3" />
        <twig:Pagination:Item href="#" label="Next" />
    </twig:Pagination>

    <twig:Pagination ariaLabel="Article pages with a non-interactive current page" class="mt-3">
        <twig:Pagination:Item href="#" label="1" />
        <twig:Pagination:Item label="2" active />
        <twig:Pagination:Item href="#" label="3" />
    </twig:Pagination>
</div>

Disabled

Disable unavailable navigation items without leaving an unusable link in the keyboard tab order.

100%
Loading...
<twig:Pagination ariaLabel="Article pages">
    <twig:Pagination:Item label="Previous" disabled />
    <twig:Pagination:Item href="#" label="1" />
    <twig:Pagination:Item href="#" label="2" active />
    <twig:Pagination:Item href="#" label="3" />
    <twig:Pagination:Item href="#" label="Next" />
</twig:Pagination>

Sizing

Use Bootstrap's large and small pagination sizes.

100%
Loading...
<div class="d-flex flex-column gap-3">
    <twig:Pagination size="lg" ariaLabel="Large pagination example">
        <twig:Pagination:Item label="1" active />
        <twig:Pagination:Item href="#" label="2" />
        <twig:Pagination:Item href="#" label="3" />
    </twig:Pagination>

    <twig:Pagination size="sm" ariaLabel="Small pagination example">
        <twig:Pagination:Item label="1" active />
        <twig:Pagination:Item href="#" label="2" />
        <twig:Pagination:Item href="#" label="3" />
    </twig:Pagination>
</div>

Alignment

Align the pagination links with Bootstrap flexbox utilities through the align prop.

100%
Loading...
<div class="d-flex flex-column gap-3">
    <twig:Pagination align="center" ariaLabel="Centered page navigation example">
        <twig:Pagination:Item label="Previous" disabled />
        <twig:Pagination:Item href="#" label="1" />
        <twig:Pagination:Item href="#" label="2" />
        <twig:Pagination:Item href="#" label="3" />
        <twig:Pagination:Item href="#" label="Next" />
    </twig:Pagination>

    <twig:Pagination align="end" ariaLabel="End-aligned page navigation example">
        <twig:Pagination:Item label="Previous" disabled />
        <twig:Pagination:Item href="#" label="1" />
        <twig:Pagination:Item href="#" label="2" />
        <twig:Pagination:Item href="#" label="3" />
        <twig:Pagination:Item href="#" label="Next" />
    </twig:Pagination>
</div>

API Reference

<twig:Pagination>

Prop Type Default
size 
'sm'|'lg'|null null
align 
'start'|'center'|'end'|null null
ariaLabel 
string 'Pagination'
Block Description
content The pagination items, typically Pagination:Item components.

<twig:Pagination:Item>

Prop Type Default
active 
boolean false
disabled 
boolean false
href 
string|null null
label 
string ''
ariaLabel 
string|null null
Block Description
content The page link content.