Skip to content

Instantly share code, notes, and snippets.

@maxpou
Last active October 21, 2025 01:09
Show Gist options
  • Select an option

  • Save maxpou/612359ed4af4cc5c4f06 to your computer and use it in GitHub Desktop.

Select an option

Save maxpou/612359ed4af4cc5c4f06 to your computer and use it in GitHub Desktop.
Example of pagination with Twig
{#
Parameters:
* nbPages (int): number of pages
* currentPage (int): current pages
* url (string): route name & query (string): route parameter
ex: list/page-5?q=myFilter (5 = page and query = myFilter)
#}
{% spaceless %}
{% if nbPages > 1 %}
{# Number of page around current page+1 #}
{% set nearbyPagesLimit = 4 %}
<div>
<ul class="pagination">
{% if currentPage != 1 %}
<li>
<a href="{{ path(url, { 'page': 1, 'q': query }) }}">First</a>
</li>
{% endif %}
{% for i in 1..nbPages %}
{% if 0 == (currentPage - nearbyPagesLimit) - loop.index %} {# dot before #}
<li class="disabled"><a href="#">...</a></li>
{% elseif 0 == (currentPage + nearbyPagesLimit) - loop.index %} {# dot after #}
<li class="disabled"><a href="#">...</a></li>
{% elseif 0 < (currentPage - nearbyPagesLimit) - loop.index %} {# hide all before #}
{% elseif 0 > (currentPage + nearbyPagesLimit) - loop.index %} {# hide all after #}
{% else %}
<li {% if currentPage == loop.index %} class="active"{% endif %}>
<a href="{{ path(url, { 'page': loop.index, 'q': query }) }}">{{ loop.index }}</a>
</li>
{% endif %}
{% endfor %}
{% if currentPage != nbPages %}
<li>
<a href="{{ path(url, { 'page': nbPages, 'q': query }) }}">Last</a>
</li>
{% endif %}
</ul>
</div>
{% endif %}
{% endspaceless %}
@Qrzysio

Qrzysio commented Jan 31, 2020

Copy link
Copy Markdown

Updated.

Dropping page number for page no. 1.

BEFORE:
www.example.com/articles/page-1
www.example.com/articles/page-2
www.example.com/articles/page-3

NOW:
www.example.com/articles/
www.example.com/articles/page-2
www.example.com/articles/page-3

{% macro pagination(total, current, url, nearbyPagesLimit = 4) %}

    {# Create "main_url" variable with link for the first page #}
    {% set foo = url|split('/') %}
    {% set foo = foo|slice(0, -1) %}
    {% set main_url = foo|join('/') ~ "/" %}

    {% apply spaceless %}
        {% if total > 1 %}
            <div class="row">
                <div class="col-md-12 blog-posts">
                    <nav>
                    <ul class="pagination theme-colored2">
                        {% if current > 1 %}
                            <li><a class="prev page-numbers" href="{{ (url ~ (current-1))|e }}/">&laquo;</a></li>
                        {% endif %}

                        {% for i in 1..total %}
                            {% if 0 == (current - nearbyPagesLimit) - loop.index %}
                                <li><a class="page-numbers" href="{{ (url ~ 1)|e }}/">1</a></li>
                                {% if 1 != loop.index %}
                                    <li><a href="#"><span class="page-numbers">...</span></a></li>
                                {% endif %}
                            {% elseif 0 == (current + nearbyPagesLimit) - loop.index  and (current + nearbyPagesLimit) < total %}
                                <li><a href="#"><span class="page-numbers">...</span></a></li>
                            {% elseif 0 < (current - nearbyPagesLimit) - loop.index %}
                                <!-- #1 -->
                            {% elseif 0 > (current + nearbyPagesLimit) - loop.index %}
                                <!-- #2 -->
                            {% else %}
                                    {% if current == loop.index  %}
                                        <li class="active"><a href="#"><span class="page-numbers" aria-current="page">{{ loop.index }}</span></a></li>
                                    {% else %}
                                        {% if loop.index == 1 %}
                                            <li><a class="page-numbers" href="{{ main_url }}">{{ loop.index }}</a></li>
                                        {% else %}
                                            <li><a class="page-numbers" href="{{ url ~ loop.index }}/">{{ loop.index }}</a></li>
                                        {% endif %}
                                    {% endif %}
                            {% endif %}
                        {% endfor %}
                        {% if current != total  and (current + nearbyPagesLimit) < total %}
                            <li><a class="page-numbers" href="{{ (url ~ total)|e }}/">{{ total }}</a></li>
                        {% endif %}
                        {% if current < total %}
                            <li><a class="next page-numbers" href="{{ (url ~ (current+1))|e }}/">&raquo;</a></li>
                        {% endif %}
                    </ul>
                    </nav>
                </div>
            </div>
        {% endif %}
    {% endapply %}
{% endmacro %}

{{ _self.pagination(total, current, url) }}

@db306

db306 commented Apr 20, 2020

Copy link
Copy Markdown

@Qrzysio you're missing a </nav>

@Qrzysio

Qrzysio commented Apr 20, 2020

Copy link
Copy Markdown

Correct. Thanks a lot. I've updated the code.

@skylord123

Copy link
Copy Markdown
{% macro pagination(total, current, pageQueryParam = 'page', nearbyPagesLimit = 4) %}
    {% if total > 1 %}
        <ul class="pagination my-4">
            {% for i in 1..total %}
                {% if 0 == (current - nearbyPagesLimit) - loop.index %}
                    <li class="page-item"><a href="{{ (app.request.pathInfo ~ '?' ~ (app.request.query.all|merge({(pageQueryParam): 1})|url_encode))|e }}" class="page-link">1</a></li>
                    {% if 1 != loop.index %}
                        <li class="page-item">
                            <a href="javascript:void(0)" class="page-link">...</a>
                        </li>
                    {% endif %}
                {% elseif 0 == (current + nearbyPagesLimit) - loop.index  and (current + nearbyPagesLimit) < total %}
                    <li class="page-item">
                        <a href="javascript:void(0)" class="page-link">...</a>
                    </li>
                {% elseif 0 < (current - nearbyPagesLimit) - loop.index %}
                {% elseif 0 > (current + nearbyPagesLimit) - loop.index %}
                {% else %}
                    <li class="page-item{{ current == loop.index ? ' active' }}">
                        <a href="{{ (app.request.pathInfo ~ '?' ~ (app.request.query.all|merge({(pageQueryParam): loop.index})|url_encode))|e }}" class="page-link">{{ loop.index }}</a>
                    </li>
                {% endif %}
            {% endfor %}
            {% if current != total  and (current + nearbyPagesLimit) < total %}
                <li class="page-item"><a href="{{ (app.request.pathInfo ~ '?' ~ (app.request.query.all|merge({(pageQueryParam): total})|url_encode))|e }}" class="page-link">{{ total }}</a></li>
            {% endif %}
        </ul>
    {% endif %}
{% endmacro %}

I didn't like how the page had to be passed in with the macro so I decided to make it use the app.request object to retain all query parameters and only modify the page query parameter (with the option to change what the page query param is called). This assumes that pagination will always be hitting the same route the user is already on (which is true in my case but if not for your case you can easily modify the macro).

I also changed it to display via bootstrap 4.

@cadot-info

Copy link
Copy Markdown

thanks, for your share ;-)

@cadot-eu

cadot-eu commented Apr 20, 2022

Copy link
Copy Markdown

Pagination with a variable.

	<nav aria-label="Page navigation example">
		<ul
			class="pagination justify-content-center">
			{# precedent #}
			<li class="page-item {{ app.request.query.get('page')==1 or app.request.query.get('page')==0 ? 'disabled' }} ">
				{% if app.request.query.get('page') is defined and app.request.query.get('page')>1 %}
					{% set precedent = app.request.query.get('page') -1 %}
				{% else %}
					{% set precedent = 1 %}
				{% endif %}
				<a class="page-link" href="{{ path('¤entity¤_index',{'page':precedent,'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')}) }}">
					<span aria-hidden="true">&laquo;</span>
				</a>
			</li>
			{% if pagesMaxi/10>6 %}
				{# 1 #}
				<li class="page-item">
					<a class="page-link" href="{{ path('¤entity¤_index',{'page':1,'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')}) }}">1</a>
				</li>
				{# pages millieu #}
				{% for item in 1..5 %}
					<li class="page-item">
						<a class="page-link " href="{{ path('¤entity¤_index',{'page':(pagesMaxi/10/6*loop.index)|number_format(0),'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')}) }}">{{(pagesMaxi/10/6*loop.index)|number_format(0)}}</a>
					</li>
				{% endfor %}
				{# maxi #}
				<li class="page-item">
					<a class="page-link" href={{ path('¤entity¤_index',{'page':(pagesMaxi/10)|number_format(0),'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')} ) }}>{{(pagesMaxi/10)|number_format(0)}}</a>
				</li>
			{% else %}
				{% for item in 1..(pagesMaxi/10)|number_format(0) %}
					<li class="page-item">
						<a class="page-link " href="{{ path('¤entity¤_index',{'page':loop.index,'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')}) }}">{{loop.index}}</a>
					</li>
				{% endfor %}
			{% endif %}
			{# suivant #}
			<li class="page-item {{ app.request.query.get('page') >= (pagesMaxi/10)|number_format ? 'disabled' }}">
				{% if app.request.query.get('page')==0 %}
					{% set suivant = 2 %}
				{% else %}
					{% set suivant = app.request.query.get('page') +1 %}
				{% endif %}
				<a class="page-link" href="{{ path('¤entity¤_index',{'page':suivant,'tri':app.request.query.get('tri'),'ordre':app.request.query.get('ordre')}) }}">
					<span aria-hidden="true">&raquo;</span>
				</a>
			</li>
		</ul>
	</nav>

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