Skip to content

Instantly share code, notes, and snippets.

@SimonSimCity
Last active May 16, 2025 07:13
Show Gist options
  • Select an option

  • Save SimonSimCity/4594748 to your computer and use it in GitHub Desktop.

Select an option

Save SimonSimCity/4594748 to your computer and use it in GitHub Desktop.
A gist for pagination in Twig, based on the total number of pages, the current page and some URL-settings.
{#
Source: http://dev.dbl-a.com/symfony-2-0/symfony2-and-twig-pagination/
Updated by: Simon Schick <simonsimcity@gmail.com>
Parameters:
* currentFilters (array) : associative array that contains the current route-arguments
* currentPage (int) : the current page you are in
* paginationPath (string) : the route name to use for links
* showAlwaysFirstAndLast (bool) : Always show first and last link (just disabled)
* lastPage (int) : represents the total number of existing pages
#}
{% spaceless %}
{% if lastPage > 1 %}
{# the number of first and last pages to be displayed #}
{% set extremePagesLimit = 3 %}
{# the number of pages that are displayed around the active page #}
{% set nearbyPagesLimit = 2 %}
<div class="pagination">
{% if currentPage > 1 %}
<a href="{{ path(paginationPath, currentFilters|merge({page: currentPage-1})) }}">Previous</a>
{% for i in range(1, extremePagesLimit) if ( i < currentPage - nearbyPagesLimit ) %}
<a href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a>
{% endfor %}
{% if extremePagesLimit + 1 < currentPage - nearbyPagesLimit %}
<span class="sep-dots">...</span>
{% endif %}
{% for i in range(currentPage-nearbyPagesLimit, currentPage-1) if ( i > 0 ) %}
<a href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a>
{% endfor %}
{% elseif showAlwaysFirstAndLast %}
<span class="disabled">Previous</span>
{% endif %}
<a href="{{ path(paginationPath, currentFilters|merge({ page: currentPage })) }}"
class="active">{{ currentPage }}</a>
{% if currentPage < lastPage %}
{% for i in range(currentPage+1, currentPage + nearbyPagesLimit) if ( i <= lastPage ) %}
<a href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a>
{% endfor %}
{% if (lastPage - extremePagesLimit) > (currentPage + nearbyPagesLimit) %}
<span class="sep-dots">...</span>
{% endif %}
{% for i in range(lastPage - extremePagesLimit+1, lastPage) if ( i > currentPage + nearbyPagesLimit ) %}
<a href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a>
{% endfor %}
<a href="{{ path(paginationPath, currentFilters|merge({page: currentPage+1})) }}">Next</a>
{% elseif showAlwaysFirstAndLast %}
<span class="disabled">Next</span>
{% endif %}
</div>
{% endif %}
{% endspaceless %}
@brittonwalker

Copy link
Copy Markdown

@SimonSimCity Thank you so much for making this, you are my hero.

@GSAnthony

Copy link
Copy Markdown

Thank you for this gist and all other versions of this gist.

@ardentsword

Copy link
Copy Markdown

Another slight improvement for the issue I ran into today, when currentFilters is not defined, get the current route params:

{% if currentFilters is not defined %}{% set currentFilters = app.request.attributes.get('_route_params') %}{% endif %}

@ra0ued

ra0ued commented Jan 30, 2018

Copy link
Copy Markdown

Thank you!

@ljayz

ljayz commented Feb 4, 2018

Copy link
Copy Markdown

Thank you!

@alexcarpenter

Copy link
Copy Markdown

Might add aria-current="page" to the active pagination link for better accessibility.

@Grawl

Grawl commented Jul 11, 2018 •

Copy link
Copy Markdown

I just updated it for my next work project using UIkit.

I added scope object and used all options from it.

Also I added two methods of URL building:

  • page number in URL query parameters like /news?page=4
  • page number directly after paginationPath option like /news/4

I replaced path() function with macros with |url_encode filter to convert an object to URL query string.

Here's a fork: https://gist.github.com/Grawl/5ebe19aa808e9b4371938a697b1597e8

Will be good to add an option to modify page URL query key to get /news?p=4 or /news?pagination=4

@KaiCMueller

Copy link
Copy Markdown

Thank you for the great work! I adapted your approach to build a paginator for the Symfony EasyAdmin bundle: https://gist.github.com/KaiCMueller/6692ee84f51341acf582e2103f05f3d4

@Matt-PMCT

Copy link
Copy Markdown

As a warning to newer users Twig is deprecating the if appended to the end of the for statements

@ulab

ulab commented Feb 2, 2021 •

Copy link
Copy Markdown

Twig 2.10 deprecated the inline if statements in for loops. You now have to use filters instead or move the if inside of the loop:

https://twig.symfony.com/doc/2.x/filters/filter.html#filter

@AliAkinK

Copy link
Copy Markdown

I wish there could be an update for this without the depreciated for statements.

@ardentsword

Copy link
Copy Markdown

@ulab @AliAkinK I actually fixed this problem years ago but never thought about uploading it here, if anyone is still interested:
I'm actually still actively using it, and it works quite well :)

{#
  Source: http://dev.dbl-a.com/symfony-2-0/symfony2-and-twig-pagination/
  Updated by: Simon Schick <simonsimcity@gmail.com>
  Parameters:
    * currentFilters (array) : associative array that contains the current route-arguments
    * currentPage (int) : the current page you are in
    * paginationPath (string) : the route name to use for links
    * showAlwaysFirstAndLast (bool) : Always show first and last link (just disabled)
    * lastPage (int) : represents the total number of existing pages
#}
{% apply spaceless %}
    {% if lastPage > 1 %}

        {# the number of first and last pages to be displayed #}
        {% set extremePagesLimit = 3 %}

        {# the number of pages that are displayed around the active page #}
        {% set nearbyPagesLimit = 2 %}

        {% if currentFilters is not defined %}{% set currentFilters = app.request.attributes.get('_route_params')|merge(app.request.query.all) %}{% endif %}
        {% if paginationPath is not defined %}{% set paginationPath = app.request.attributes.get('_route') %}{% endif %}
        {% if showAlwaysFirstAndLast is not defined %}{% set showAlwaysFirstAndLast = true %}{% endif %}

        <nav aria-label="Page navigation">
        <ul class="pagination">
            {% if currentPage > 1 %}
                <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: currentPage-1})) }}">Previous</a></li>

                {% for i in range(1, extremePagesLimit) | filter(i => i < currentPage - nearbyPagesLimit ) %}
                    <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a></li>
                {% endfor %}

                {% if extremePagesLimit + 1 < currentPage - nearbyPagesLimit %}
                    <span class="sep-dots">...</span>
                {% endif %}

                {% for i in range(currentPage-nearbyPagesLimit, currentPage-1) | filter(i => i > 0 ) %}
                    <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a></li>
                {% endfor %}
            {% elseif showAlwaysFirstAndLast %}
                <li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
            {% endif %}

            <li class="page-item active"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({ page: currentPage })) }}">{{ currentPage }}</a></li>

            {% if currentPage < lastPage %}
                {% for i in range(currentPage+1, currentPage + nearbyPagesLimit) | filter(i => i <= lastPage) %}
                    <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a></li>
                {% endfor %}

                {% if  (lastPage - extremePagesLimit) > (currentPage + nearbyPagesLimit) %}
                    <span class="sep-dots">...</span>
                {% endif %}

                {% for i in range(lastPage - extremePagesLimit+1, lastPage) | filter( i => i > currentPage + nearbyPagesLimit ) %}
                    <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: i})) }}">{{ i }}</a></li>
                {% endfor %}

                <li class="page-item"><a class="page-link" href="{{ path(paginationPath, currentFilters|merge({page: currentPage+1})) }}">Next</a></li>
            {% elseif showAlwaysFirstAndLast %}
                <li class="page-item disabled"><a class="page-link" href="#">Next</a></li>
            {% endif %}
        </ul>
        </nav>
    {% endif %}
{% endapply %}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment