In short
CSS pagination splits long content into numbered pages. Learn to build page number links with Flexbox, active and hover states, rounded and bordered styles, and accessible markup. What is pagination and what HTML should you use?
CSS pagination splits long content into numbered pages. Learn to build page number links with Flexbox, active and hover states, rounded and bordered styles, and accessible markup.
What is pagination and what HTML should you use?
Pagination is the row of page numbers you see at the bottom of a search page or a blog: « 1 2 3 4 5 ». It splits a long list into smaller pages, like chapters in a book. Each number is a link to one page.
Pagination is navigation, so the right HTML is a <nav> element with a list of links inside. Give the <nav> an aria-label, so screen reader users hear "Pagination, navigation" and know what this group of links is. Mark the current page with aria-current="page". That attribute tells assistive technology "you are here", and it is also a perfect hook for our CSS.
<nav class="pagination" aria-label="Pagination">
<ul>
<li><a href="?page=1">1</a></li>
<li><a href="?page=2" aria-current="page">2</a></li>
<li><a href="?page=3">3</a></li>
<li><a href="?page=4">4</a></li>
<li><a href="?page=5">5</a></li>
</ul>
</nav>
Without CSS, you see a boring vertical bulleted list of numbers. Let's fix that.
Simple pagination with Flexbox
We remove the bullets, put the list items in a row with Flexbox, and turn each link into a box that is easy to tap.
.pagination ul {
display: flex;
flex-wrap: wrap;
gap: 6px;
list-style: none;
margin: 0;
padding: 0;
}
.pagination a {
display: block;
min-width: 2.5rem;
padding: 0.5rem 0.75rem;
text-align: center;
text-decoration: none;
color: #1f2937;
border-radius: 6px;
}
.pagination a:hover {
background: #e5e7eb;
}
.pagination a[aria-current="page"] {
background: #2563eb;
color: white;
font-weight: 700;
}
.pagination a:focus-visible {
outline: 3px solid #f59e0b;
outline-offset: 2px;
}
Now the numbers sit in a neat row with small gaps. Page 2 has a blue background with white bold text, so it is clear where you are. Hovering another number gives it a light grey background. min-width makes single numbers like "1" and long numbers like "12" look like equal boxes, and flex-wrap: wrap lets the row break onto two lines on a tiny phone instead of overflowing.
Tip: Style the current page with the attribute selector
a[aria-current="page"]instead of an extra.activeclass. You need the attribute for accessibility anyway, so one attribute does two jobs and they can never get out of sync.
Bordered and rounded pagination variants
Different designs need different looks. Here are two popular variants: joined boxes with borders, and round "pill" numbers.
/* Bordered: boxes joined together */
.pagination-bordered ul {
gap: 0;
}
.pagination-bordered a {
border: 1px solid #d1d5db;
border-radius: 0;
margin-left: -1px; /* stop double borders */
}
.pagination-bordered li:first-child a { border-radius: 6px 0 0 6px; }
.pagination-bordered li:last-child a { border-radius: 0 6px 6px 0; }
.pagination-bordered a[aria-current="page"] {
border-color: #2563eb;
position: relative; /* bring the blue border on top of its neighbors */
}
/* Rounded: circles */
.pagination-rounded a {
border-radius: 50%;
width: 2.5rem;
height: 2.5rem;
padding: 0;
display: grid;
place-items: center;
}
<nav class="pagination pagination-bordered" aria-label="Pagination">
<ul>
<li><a href="?page=1">1</a></li>
<li><a href="?page=2" aria-current="page">2</a></li>
<li><a href="?page=3">3</a></li>
</ul>
</nav>
The bordered version looks like one long strip divided into cells, with rounded corners only on the outside ends. The margin-left: -1px pulls each box one pixel to the left, so two borders overlap and look like one thin line. The rounded version (add pagination-rounded instead) turns each number into a circle, with display: grid and place-items: center putting the number exactly in the middle.
Previous and next links, and the ellipsis
Real pagination usually has "Previous" and "Next" links, and it skips the middle pages with "…" when there are many pages.
<nav class="pagination" aria-label="Pagination">
<ul>
<li><a href="?page=3" rel="prev">‹ Previous</a></li>
<li><a href="?page=1">1</a></li>
<li><span class="dots">…</span></li>
<li><a href="?page=4" aria-current="page">4</a></li>
<li><a href="?page=5">5</a></li>
<li><span class="dots">…</span></li>
<li><a href="?page=20">20</a></li>
<li><a href="?page=5" rel="next">Next ›</a></li>
</ul>
</nav>
.pagination .dots {
display: block;
padding: 0.5rem 0.25rem;
color: #6b7280;
}
.pagination a[rel="prev"],
.pagination a[rel="next"] {
font-weight: 600;
}
.pagination .is-disabled {
display: block;
padding: 0.5rem 0.75rem;
color: #9ca3af;
cursor: not-allowed;
}
You see "‹ Previous 1 … 4 5 … 20 Next ›". The dots are a plain <span>, not a link, because there is nothing to click. On page 1 there is no previous page, so instead of a link you can write <span class="is-disabled">‹ Previous</span>. It shows in grey and can't be clicked or focused.
Common mistake: Using
<button>or<div>elements with click handlers for page numbers. Each page has its own URL, so use real links. Then people can open a page in a new tab, share it, and search engines can find every page.
| Part | HTML | Why |
|---|---|---|
| Wrapper | <nav aria-label="Pagination"> | Announces a named navigation area |
| Numbers | <ul> with <li><a href> | A list of real, shareable links |
| Current page | aria-current="page" | Says "you are here" and styles the active page |
| Gaps | <span>…</span> | Not clickable, so not a link |
| Prev / next | rel="prev" / rel="next" | Describes the link's relationship |
Practice exercise
Build pagination for a blog with 10 pages where the reader is on page 6. Show Previous, 1, …, 5, 6, 7, …, 10 and Next. Make it the rounded variant, and make the current page green instead of blue.
Hint: copy the base .pagination CSS, add the pagination-rounded class, and change the background in the a[aria-current="page"] rule. Prev and next links have text, so give them width: auto and some side padding inside the rounded variant.
Key takeaways
- Wrap pagination in
<nav aria-label="Pagination">with a list of real links. - Use
display: flex,gapandflex-wrapto lay out the numbers in a row. - Mark and style the current page with
aria-current="page". - Bordered variants use negative margins to avoid double borders; rounded variants use
border-radius: 50%. - Use a plain
<span>for "…" and for a disabled Previous or Next.