Pagination — <mv-pagination>
Numbered pagination with ellipses and a constant number of slots (the bar never jumps), previous/next, a compact “Page x of y” mode; the CSS styles work on their own for server-rendered links.
Category Navigation Type Web Component (<mv-pagination>) Status stable Keywords pagination, pages, pager, ellipsis, navigation, table
When to use
A long list, table or set of search results must be split into numbered pages Server-rendered page links need consistent styling without JavaScript Space is tight and a compact Page x of y control is enough
Avoid when
A table also needs sorting, search and filters wired to its paging → use Data table instead The pages are steps of a single process that must be completed in order → use Stepper instead The content is a feed people browse rather than a set where they seek a specific page
Get Pagination. Included in every plan: the Pro pack ships all 226 components with the CLI and the MCP server used below.
See plans
Install
node scripts/add.mjs pagination --out ./src/marvelous
AI agent with the Marvelous UI MCP server: install_components({ slugs: ["pagination"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Files copied (dependencies included): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, components/pagination/pagination.js, components/pagination/pagination.css.
Usage
Canonical markup — start from it and customize with attributes, data-* and CSS variables:
<div style="display:grid;gap:1.75rem;justify-items:center;width:100%">
<div style="display:grid;gap:.75rem;justify-items:center">
<mv-pagination id="pg-demo-main" total="20" page="3"></mv-pagination>
<p id="pg-demo-status" style="margin:0;font-size:.8125rem;color:var(--mv-fg-muted)" aria-live="polite">Showing 21–30 of 196 results</p>
</div>
<mv-pagination total="12" page="6" data-variant="outline" hide-labels siblings="2"></mv-pagination>
<mv-pagination total="8" page="2" data-variant="solid" data-size="sm"></mv-pagination>
<mv-pagination total="42" page="7" data-variant="pill" compact hide-labels></mv-pagination>
<!-- CSS only: server-rendered links -->
<nav class="mv-pagination" aria-label="Article pagination">
<ul>
<li><a class="mv-pagination-prev" href="#" aria-label="Previous page"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>Previous</a></li>
<li><a class="mv-pagination-item" href="#">1</a></li>
<li><a class="mv-pagination-item" href="#" aria-current="page">2</a></li>
<li><a class="mv-pagination-item" href="#">3</a></li>
<li aria-hidden="true"><span class="mv-pagination-ellipsis"></span></li>
<li><a class="mv-pagination-item" href="#">9</a></li>
<li><a class="mv-pagination-next" href="#" aria-label="Next page">Next<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6"/></svg></a></li>
</ul>
</nav>
</div>
<script type="module">
const pager = document.getElementById("pg-demo-main");
const status = document.getElementById("pg-demo-status");
pager?.addEventListener("mv-change", (e) => {
const from = (e.detail.page - 1) * 10 + 1;
const to = Math.min(196, from + 9);
status.textContent = `Showing ${from}–${to} of 196 results`;
});
</script>
API
Attributes
Name Type Default Description totalnumber 1Total number of pages. pagenumber 1Current page (1-based, clamped). Updated on click. siblingsnumber 1Pages shown on each side of the current page. boundariesnumber 1Pages always shown at the start and end. compactboolean Replaces the numbers with “Page x of y”. hide-labelsboolean Icon-only Previous/Next (labels kept for screen readers). prev-label / next-labelstring Previous / NextArrow labels. labelstring PaginationLandmark aria-label. data-variantoutline | solid | pill (ghost)Style: ghost buttons with an outlined current page; all outlined; accent-filled current page; rounded track. data-sizesm Smaller buttons.
Properties
Name Type Description pageCountnumber Effective page count (read-only). currentnumber Clamped current page (read-only).
Methods
Name Description goTo(page)Goes to a page and emits mv-change. next() / prev()Next / previous page. MvPagination.range(page, total, siblings, boundaries)Static: list of numbers and “ellipsis” entries to render (reusable server-side).
Events
Name Description mv-changeCancelable (preventDefault keeps the page). detail: { page, previous }.
CSS classes
Name Description mv-paginationOn <nav aria-label> (or set automatically on the element). mv-pagination-itemPage button or link; aria-current="page" for the current page. mv-pagination-prev / -nextArrows; aria-disabled="true" at either end. mv-pagination-ellipsisEllipsis (automatic icon when empty), inside an <li aria-hidden="true">. mv-pagination-status“Page 3 of 20” text.
CSS variables
Name Default Description --mv-pagination-radiusvar(--mv-radius-md)Button radius.
Accessibility
Labeled navigation landmark, aria-current="page" on the current page, each number announced as “Page n”. Arrows at either end stay focusable with aria-disabled so focus is never lost; after a page change, focus stays on the same button. In compact mode, the status is announced (aria-live).