jekyll pagination on category pages without plugins
Why Category Pagination is Important
When your blog starts to grow, category pages can become long and overwhelming. Pagination helps break these pages into manageable parts, improving both user experience and crawlability. Unfortunately, Jekyll does not support pagination out of the box on custom category pages unless you use plugins — which are not supported on GitHub Pages. So, we’ll explore a clean, plugin-free method.
The Challenge with Native Pagination
Jekyll’s built-in paginator only works on the main index page. It doesn't natively allow you to paginate custom collections or category archives. To overcome this, we need to work with some Liquid logic and page templates.
Step 1: Create a Category Page Template
First, you’ll need a category layout (e.g., category.html) that can receive a category name via front matter.
--- layout: default title: Category permalink: /category/:name/ ---
Add the following to load all posts with a specific category:
{% raw %}
{% assign category = page.category %}
{% assign posts = site.posts | where_exp: "item", "item.categories contains category" %}
{% endraw %}
Step 2: Manual Pagination Logic
We can’t use paginator outside of index.html, but we can paginate manually using Liquid’s offset and limit. For example, to paginate 5 posts per page:
{% raw %}
{% assign page_num = page.page_num | default: 1 %}
{% assign per_page = 5 %}
{% assign start_index = per_page | times: page_num | minus: per_page %}
{% assign paginated_posts = posts | slice: start_index, per_page %}
{% endraw %}
Then loop through them as usual:
{% raw %}
{% for post in paginated_posts %}
<h3><a href="{{ post.url }}">{{ post.title }}</a></h3>
{% endfor %}
{% endraw %}
Step 3: Generate Paginated Pages Manually
This approach requires creating static paginated category pages manually using page_num as a front matter variable. For example:
--- layout: category title: "SEO Articles - Page 1" permalink: /category/seo/page1/ category: seo page_num: 1 ---
--- layout: category title: "SEO Articles - Page 2" permalink: /category/seo/page2/ category: seo page_num: 2 ---
This is repetitive, but works well for evergreen blogs that don’t publish too frequently. You can automate the page generation offline using a script or generator if needed.
Step 4: Add Pagination Navigation
Next, create navigation links manually between pages using Liquid:
{% raw %}
<div class="pagination">
{% if page.page_num > 1 %}
<a href="/category/{{ page.category }}/page{{ page.page_num | minus: 1 }}/">Previous</a>
{% endif %}
<span>Page {{ page.page_num }}</span>
{% assign total_pages = posts.size | divided_by: per_page | plus: 1 %}
{% if page.page_num < total_pages %}
<a href="/category/{{ page.category }}/page{{ page.page_num | plus: 1 }}/">Next</a>
{% endif %}
</div>
{% endraw %}
Use Case: Multi-Niche Blog on GitHub Pages
Consider a tech blog with hundreds of articles across categories like SEO, Email Marketing, and Web Development. Using this manual pagination system, the blog owner separated each category into 3–5 page chunks. This not only improved page load time but also resulted in 20% more category clicks via Google due to better indexing and internal link structuring. No plugins or paid platforms involved.
Tips for Better UX
- Keep URLs clean and consistent: use
/category/seo/page2/format. - Add canonical tags if necessary to avoid duplicate content issues.
- Use clear navigation between pages to prevent dead ends.
- Test pagination experience on mobile devices and slow connections.
Drawbacks of Manual Pagination
While effective, this method has a few downsides:
- Requires manual creation or scripting.
- Not ideal for high-frequency publishing.
- No built-in support for dynamic page generation.
Conclusion
Despite limitations, implementing category-based pagination in Jekyll without plugins is achievable and worth the effort for better SEO and user navigation. By slicing collections manually and pre-building pages, you retain full control and remain fully compatible with GitHub Pages.