SEO-Friendly Pagination
Crawlers don't scroll and don't click “Load more” buttons. If your list only grows through infinite scroll, everything after the first batch may never be found. Pagination with real links fixes that — and a “show more” link can look just as modern.
- JavaScript
- 0 KB
- Built with
- Real URLs,
<nav>,aria-current, server-side paging - Browsers
- All — it's links
- Updated
It can be. Search engine crawlers don't scroll down or press buttons, so items that only appear after scrolling or clicking “Load more” may never be crawled. Give every page of a list its own URL — ?page=2, ?page=3 — and link to it with a normal <a href>. A “Load more” control should be such a link too.
Live demo: real pagination
All 18 patterns of this library, six per page. This is not a simulation: each page is generated on the server and has its own URL — look at your address bar after clicking. “Show the next 6” and the page numbers are ordinary links.
How SEO-friendly pagination works
- One URL per page
Page 1 is the clean URL, page 2 is
?page=2, and so on. Each one can be crawled, linked, bookmarked and shared. - Links, not buttons
Every control is an
<a href>. Crawlers follow links; they don't press buttons that run scripts. - “Show more” is a link too
It points to the next page. Without JavaScript it simply opens it. With JavaScript, you can catch the click and append the next items in place — the link stays for crawlers.
- An ordered list that keeps counting
<ol start="7">on page 2 numbers the items 7 to 12, so the position stays clear for every visitor and screen reader. - The current page is marked
aria-current="page"on the active number, plus labels like “Page 2, current page” for screen readers. - No dead ends
“Previous” on page 1 and “Next” on the last page aren't links. This demo shows page 1 for an unknown page number; on a real site, a page beyond the end should return a 404, so search engines don't index empty pages.
Copy the HTML and CSS
This is the HTML the server generated for page 3 — exactly what you see in the demo. Your CMS or shop system produces the same structure for each page. All class names start with ui-.
<div class="ui-paged">
<p class="ui-paged__status">Page 3 of 3</p>
<ol class="ui-paged__list" start="13">
<li><a href="/lab/css-first-ui/language-switcher-hreflang/">Language Switcher with hreflang</a></li>
<li><a href="/lab/css-first-ui/lazy-loading-images-without-javascript/">Lazy Loading Images Without JavaScript</a></li>
<li><a href="/lab/css-first-ui/scroll-animations-without-javascript/">Scroll Animations Without JavaScript</a></li>
<li><a href="/lab/css-first-ui/filter-without-javascript/">Content Filter Without JavaScript</a></li>
<li><a href="/lab/css-first-ui/pagination-seo/">SEO-Friendly Pagination</a></li>
<li><a href="/lab/css-first-ui/table-of-contents-anchor-links/">Table of Contents & Anchor Links</a></li>
</ol>
<p class="ui-paged__end">That's all 18 patterns.</p>
<nav class="ui-pager" aria-label="Pattern list pages">
<ul>
<li><a href="/lab/css-first-ui/pagination-seo/?page=2#demo" rel="prev">‹ Previous</a></li>
<li><a href="/lab/css-first-ui/pagination-seo/#demo" aria-label="Page 1">1</a></li>
<li><a href="/lab/css-first-ui/pagination-seo/?page=2#demo" aria-label="Page 2">2</a></li>
<li><a href="/lab/css-first-ui/pagination-seo/?page=3#demo" aria-current="page" aria-label="Page 3, current page">3</a></li>
<li><span aria-hidden="true">Next ›</span></li>
</ul>
</nav>
</div>/* SEO-friendly pagination — no JavaScript.
Every page is a real URL, every control a real link.
"Show more" is a link to the next page, so crawlers can follow it. */
.ui-paged {
--ui-text: #1b1f3b;
--ui-muted: #5d6280;
--ui-accent: #4f46e5;
font: 500 0.9375rem/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
color: var(--ui-text);
}
.ui-paged__status {
margin: 0 0 0.6rem;
color: rgb(255 255 255 / 0.75);
font-size: 0.875rem;
}
.ui-paged__list {
display: grid;
gap: 0.5rem;
margin: 0 0 1rem;
padding: 0;
list-style: none;
}
.ui-paged__list a {
display: block;
padding: 0.8rem 1rem;
border-radius: 10px;
background: #fff;
color: var(--ui-text);
text-decoration: none;
}
.ui-paged__list a:hover {
color: var(--ui-accent);
text-decoration: underline;
}
.ui-paged a:focus-visible {
outline: 2px solid #fff;
outline-offset: 2px;
}
/* "Show more": a link that looks like a button */
.ui-paged__more {
display: block;
margin: 0 0 1.25rem;
padding: 0.75rem 1rem;
border-radius: 10px;
background: var(--ui-accent);
color: #fff;
font-weight: 600;
text-align: center;
text-decoration: none;
}
.ui-paged__end {
margin: 0 0 1.25rem;
color: rgb(255 255 255 / 0.75);
text-align: center;
}
/* Page numbers */
.ui-pager ul {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.35rem;
margin: 0;
padding: 0;
list-style: none;
}
.ui-pager a,
.ui-pager span {
display: block;
min-width: 2.5rem;
padding: 0.5rem 0.75rem;
border-radius: 8px;
text-align: center;
text-decoration: none;
}
.ui-pager a {
background: rgb(255 255 255 / 0.12);
color: #fff;
}
.ui-pager a:hover {
background: rgb(255 255 255 / 0.25);
}
.ui-pager a[aria-current="page"] {
background: #fff;
color: var(--ui-text);
font-weight: 700;
}
.ui-pager span {
color: rgb(255 255 255 / 0.35);
}The server side, in a few lines
<?php
// Minimal server-side pagination (PHP). The same idea works in any CMS.
$perPage = 6;
$page = max(1, (int) ($_GET['page'] ?? 1));
$total = count($items);
$pages = (int) ceil($total / $perPage);
if ($page > $pages) { http_response_code(404); exit; } // no empty pages
$visible = array_slice($items, ($page - 1) * $perPage, $perPage);
// In the <head>: every page canonicalises to ITSELF, not to page 1
$self = $page === 1 ? '/products/' : '/products/?page=' . $page;
echo '<link rel="canonical" href="https://www.example.com' . $self . '">';
// And give each page its own title: "Products – page 2 of 5"Pagination rules for Google and Baidu
Every paginated page canonicalises to itself. Page 2 should not point its canonical tag to page 1 — that tells search engines to ignore page 2 and, with it, the links to the items listed there.
Don't noindex the pages of a real listing. Products that are only linked from page 4 need page 4 to be crawled. (This lab page is the exception: its ?page=2 views are copies of an article, so they carry noindex, follow — the links are still followed.)
rel="next" and rel="prev" are optional. Google stopped using them as an indexing signal in 2019. They do no harm, and the demo keeps them on its links.
Give each page a distinct title. “Industrial pumps – page 2 of 5” is clearer in search results than five identical titles.
Baidu needs plain links even more. Baidu often reads only the HTML it downloads and crawls far fewer pages of most foreign sites than Google. Lists that grow only through scrolling or scripted buttons are, for Baidu, often just the first page.
Pagination, infinite scroll and “Load more” compared
| Feature | Pagination | “Show more” link | Infinite scroll |
|---|---|---|---|
| Crawlers reach every item | Yes | Yes | Often not |
| Works without JavaScript | Yes | Yes | No |
| Shareable position | Yes | Yes | Rarely |
| Footer reachable | Yes | Yes | Keeps moving away |
| Keyboard and screen readers | Simple | Simple | Hard to do well |
| Feels seamless | Page loads | With a little JS | Yes |
Build the paginated version first, with real links. Then, if you want, add a small script that turns “Show more” into in-place loading and updates the URL. Visitors get the seamless feel, crawlers keep the links, and the list still works when the script fails.
Questions about pagination and SEO
Is infinite scroll bad for SEO?
When it is the only way to reach items, yes: crawlers don't scroll, so later items may never be found. Combine it with paginated URLs that crawlers can follow.
Should page 2 have a canonical tag to page 1?
No. Each paginated page should point its canonical to itself. Pointing to page 1 hides the items on later pages from search engines.
Does Google still use rel="next" and rel="prev"?
Not since 2019. They are harmless, and other tools may still read them, but plain links between pages are what matters.
Is a “Load more” button bad for SEO?
A <button> that loads items with JavaScript gives crawlers nothing to follow. Make it an <a href> to the next page — it can still load items in place for visitors.
How many items should one page show?
Enough to be useful, few enough to load fast — often 12 to 48 in shops. Fewer pages mean every item is fewer clicks from the first page.
This FAQ is built without JavaScript — see the accordion pattern.
Can Baidu reach all of your products?
We audit Chinese shop and catalogue sites for items crawlers can't reach — infinite scroll, scripted filters, missing pagination — and fix the structure. Then we run Baidu SEO and marketplace placements for Western companies.
CSS-First UI Patterns are written and maintained by Jademond Digital, a Chinese digital marketing agency founded in Guangzhou in 2012. We build and audit websites for Baidu, Google and AI search — which is why we care what works without JavaScript. About us →