Skip to content

Symfony UX Pagination

Edit this page

Caution

EXPERIMENTAL This bundle is currently experimental and is likely to change, possibly significantly, before its first stable release.

Symfony UX Pagination provides one lazy, request-aware API for paginating arrays, Doctrine queries and application data sources. It renders accessible links on the server and does not require a browser-side runtime.

The bundle answers the questions that usually leak into controllers and templates:

  • Who validates ?page= or ?cursor=?
  • Will the current filters survive the next link?
  • Does this screen really need an exact total and its count query?
  • Can inserts or deletes move rows while somebody is navigating?
  • Can the controls remain ordinary links without a JavaScript lifecycle?

The paginator makes those decisions explicit while keeping one iterable result for PHP, Twig and JSON. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// src/Controller/ProductController.php
namespace App\Controller;

use App\Repository\ProductRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\UX\Pagination\PaginatorInterface;

final class ProductController extends AbstractController
{
    #[Route('/products', name: 'product_index')]
    public function __invoke(
        ProductRepository $repository,
        PaginatorInterface $paginator,
    ): Response {
        $products = $paginator
            ->cursor($repository->createQueryBuilder('product'))
            ->orderBy(['createdAt', 'id'], 'DESC')
            ->perPage(20)
            ->paginate();

        return $this->render('product/index.html.twig', [
            'products' => $products,
        ]);
    }
}
1
2
3
4
5
6
{# templates/product/index.html.twig #}
{% for product in products %}
    <article>{{ product.name }}</article>
{% endfor %}

{{ ux_pagination(products) }}

Mental model

The strategy changes the database contract, not the application architecture:

Question Choose What the result can promise
Need exact totals/page numbers? Offset Counted numbered navigation
Only need a reliable next link? Lookahead Previous/next, no exact total
Can rows change while browsing? Cursor Stable sequential traversal

Start here

Use it in an application

Ship with confidence

The package supports PHP 8.4 or later, Symfony 7.4 or 8.x, and Twig 3.10.3 or later in the Twig 3 series. Doctrine ORM/DBAL, TwigComponent and LiveComponent integrations are optional.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version