Splitting a Jekyll Post List into Numbered Pages with jekyll-paginate
Configure Jekyll's official jekyll-paginate plugin to split your post list into numbered pages, update the template to use the paginator object, and verify the generated page structure — plus the silent-failure checklist.
28 Sept 2025, 11:35 UTC

If your Jekyll blog's index page has grown into an endless scroll of every post you've ever written, the fix is pagination: split the post list into numbered pages like /blog/page2/. Jekyll ships an officially supported plugin, jekyll-paginate, that does exactly this — but it has sharp edges. It only paginates posts (not collections), it only works on one template per site, and when it's misconfigured it fails silently, producing no extra pages and no error. This guide covers the configuration, the template changes, and how to confirm the output is actually correct.
What you're building and its limits
The end state: page 1 of your post list stays at its existing URL (for example /blog/), and pages 2, 3, and beyond are generated under a path pattern you choose, with previous/next navigation links in the template.
Know the limits before you start:
- Only
site.postsis paginated. Collections, categories, and arbitrary data files are not supported. - Only one template per site can be paginated — conventionally
blog/index.html. - The paginated template must be an
.htmlfile with YAML front matter. A Markdown-only file or missing front matter means pagination silently does nothing. - If you need pagination for collections or multiple paginated pages, the third-party
jekyll-paginate-v2exists, but it is not whitelisted on GitHub Pages, so it requires building the site yourself (e.g. via CI) rather than letting GitHub Pages build it.
Prerequisites
- A Jekyll site you can build locally with
bundle exec jekyll build(Jekyll 3.x or later assumed; see the version note below). - Enough posts to paginate — or a willingness to temporarily set the page size to 1 for testing.
- The
jekyll-paginategem available to your bundle. On GitHub Pages it is one of the supported plugins, so no extra gem work is needed there; for a standalone site, addgem "jekyll-paginate"to your Gemfile and runbundle install.
Version note: in Jekyll 2.x the config key for plugins was gems:; in Jekyll 3.x and later it is plugins:. Check which your version expects — using the wrong key is one of the silent-failure causes.
Configure the plugin
In _config.yml, add the plugin and set a page size and path pattern:
plugins:
- jekyll-paginate
paginate: 5
paginate_path: "/blog/page:num/"Two details matter here:
paginateis the number of posts per page. If it is unset, pagination is off entirely — silently.paginate_pathmust contain the:numplaceholder. Without it, Jekyll has no way to name pages beyond the first, and you get build errors or pages overwriting each other.
Update the template
In the paginated template (e.g. blog/index.html), replace the site.posts loop with paginator.posts. The paginator Liquid object only exists on the paginated page; it exposes page, total_pages, previous_page, next_page, and the corresponding previous_page_path / next_page_path helpers.
{% for post in paginator.posts %}
<article>
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
<time>{{ post.date | date: "%B %-d, %Y" }}</time>
</article>
{% endfor %}
<nav class="pagination">
{% if paginator.previous_page %}
<a href="{{ paginator.previous_page_path }}">← Newer posts</a>
{% endif %}
<span>Page {{ paginator.page }} of {{ paginator.total_pages }}</span>
{% if paginator.next_page %}
<a href="{{ paginator.next_page_path }}">Older posts →</a>
{% endif %}
</nav>Wrap the links in the if checks as shown: on page 1 there is no previous page, and on the last page there is no next page, so the path helpers would otherwise produce broken links.
Verify the generated structure
Build locally from the site root (no special permissions needed):
bundle exec jekyll buildThen inspect the output directory. With the config above and 12 posts at 5 per page, you should find:
_site/blog/index.html— page 1, served from the template's own URL_site/blog/page2/index.html_site/blog/page3/index.html— the final page, with only 2 posts
A quick structural check:
find _site/blog -name index.html | sortTo make navigation easy to inspect, temporarily set paginate: 1 so every post gets its own page, then run bundle exec jekyll serve and click through the previous/next links across several pages in a browser. Confirm the post counts per page and that the first and last pages correctly omit one of the two links. Restore your real page size afterward.
When nothing is generated
Silent failure — no page2, no error — is the most common problem. Check in this order:
- Plugin key and spelling. Is
jekyll-paginatelisted under the key your Jekyll version reads (plugins:for 3.x+,gems:for 2.x), spelled exactly? paginateis set to a positive number in_config.yml.- Template file. It must end in
.htmland have YAML front matter (even empty front matter,---/---). paginate_pathcontains:num.- Enough posts. With 4 posts and
paginate: 5, only page 1 exists — correctly.
If you deploy via GitHub Pages, remember the build there uses pinned dependency versions; if local builds paginate but the deployed site doesn't, compare against the versions GitHub Pages currently pins.
Performance caveat
The plugin builds all paginated pages in memory at generation time. For a few hundred posts this is negligible; for very large archives it measurably increases build time. If build time becomes a problem, that — along with collection support — is the practical reason to evaluate jekyll-paginate-v2 and a self-managed build pipeline.
No rollback is needed for any of this: pagination is pure build output. Deleting the config keys and reverting the template loop to site.posts returns the site to a single-page list on the next build.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.