Pagination
ui-pagination renders a <nav> with Previous, Next and numbered page controls from two numbers: the current
page and the total number of pages. It always shows the first and last page, the pages within siblings of the
current one, and an ellipsis for each gap. It works in two modes:
- Without
href, Previous, Next and the page numbers are native buttons. Clicking one (or pressing Enter or Space) updates thepageattribute in place and fireschange. Use this for client-side lists. - With
href, Previous, Next and every page number are links built from the pattern, for example?page={page}.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pagination demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/pagination.html"></import-element>
<ui-pagination page="5" total="10"></ui-pagination>
</body>
</html>Examples
Siblings
siblings sets how many pages are shown on each side of the current page. The default is 1.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pagination demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
<style>
.demo-stack {
display: grid;
gap: calc(var(--spacing) * 4);
}
</style>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/pagination.html"></import-element>
<div class="demo-stack">
<ui-pagination page="10" total="20" siblings="0"></ui-pagination>
<ui-pagination page="10" total="20"></ui-pagination>
<ui-pagination page="10" total="20" siblings="2"></ui-pagination>
</div>
</body>
</html>Few pages
When every page fits, no ellipsis is shown. Previous is disabled on the first page and Next on the last.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pagination demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/pagination.html"></import-element>
<ui-pagination page="1" total="3"></ui-pagination>
</body>
</html>Links
With href, {page} in the pattern is replaced by each page number, so the numbers are crawlable links and work
without script. The component does not change page itself in this mode. The next page load sets it.
<ui-pagination page="2" total="5" href="/invoices?page={page}"></ui-pagination>Controlled from state
Bind page and total from state and read the new page from event.detail.page. Call event.preventDefault()
on change to cancel the move, for example while data is still loading.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pagination demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
<import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/pagination.html"></import-element>
<import-element from="./pagination-state-demo.html"></import-element>
<pagination-state-demo></pagination-state-demo>
</body>
</html><custom-element name="pagination-state-demo">
<script>
const list = useState({
page: 1,
total: 12,
get range() {
const start = (this.page - 1) * 10 + 1;
return `Showing ${start} to ${start + 9} of 120 invoices`;
},
});
function paged(event) {
list.page = event.detail.page;
}
</script>
<style>
:host {
display: grid;
gap: calc(var(--spacing) * 3);
justify-items: center;
}
p {
margin: 0;
font-size: var(--text-sm);
color: var(--muted-foreground);
}
</style>
<p><slot name="#list.range"></slot></p>
<ui-pagination _page="#list.page" _total="#list.total" onchange="paged(event)"></ui-pagination>
</custom-element>API
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
page |
number | 1 |
The current page. Integer, at least 1. Updated by the component when href is not set. |
total |
number | 1 |
Number of pages. Integer, at least 1. |
siblings |
number | 1 |
Pages shown on each side of the current page. Integer, at least 0. |
href |
string | URL pattern for page links. {page} is replaced by the page number. When not set, the controls are buttons that update page. |
|
label |
string | "Pagination" |
Accessible name of the <nav> landmark. |
Events
| Event | Detail | Description |
|---|---|---|
change |
{ page: number } |
Fires before the page changes, from Previous, Next or a page number. Bubbles and is cancelable. preventDefault() keeps the current page and, in href mode, stops the link from navigating. Not fired for the current page or a page out of range. |
CSS parts
| Part | Description |
|---|---|
list |
The <ul> that lays out the controls. |
previous |
The Previous control: a <button>, or an <a> in href mode. |
next |
The Next control: a <button>, or an <a> in href mode. |
page |
Each page number: a <button>, or an <a> in href mode. The current one has aria-current="page". |
Accessibility
- The component is a
<nav>landmark named bylabel, containing a list. - The current page has
aria-current="page". Ellipses are hidden from assistive technology. - Without
hrefevery control is a native<button>that Tab reaches and Enter or Space activates. Previous and Next aredisabledat the first and last page. - In
hrefmode every control is a link reached with Tab. At the first and last page, Previous and Next have nohrefandaria-disabled="true".