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.

CategoryNavigation
TypeWeb Component (<mv-pagination>)
Statusstable
Keywordspagination, pages, pager, ellipsis, navigation, table

When to use

Avoid when

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

NameTypeDefaultDescription
totalnumber1Total number of pages.
pagenumber1Current page (1-based, clamped). Updated on click.
siblingsnumber1Pages shown on each side of the current page.
boundariesnumber1Pages always shown at the start and end.
compactbooleanReplaces the numbers with “Page x of y”.
hide-labelsbooleanIcon-only Previous/Next (labels kept for screen readers).
prev-label / next-labelstringPrevious / NextArrow labels.
labelstringPaginationLandmark aria-label.
data-variantoutline | solid | pill(ghost)Style: ghost buttons with an outlined current page; all outlined; accent-filled current page; rounded track.
data-sizesmSmaller buttons.

Properties

NameTypeDescription
pageCountnumberEffective page count (read-only).
currentnumberClamped current page (read-only).

Methods

NameDescription
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

NameDescription
mv-changeCancelable (preventDefault keeps the page). detail: { page, previous }.

CSS classes

NameDescription
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

NameDefaultDescription
--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).