<script setup lang="ts">
import { APaginationEllipsis, APaginationFirst, APaginationLast, APaginationList, APaginationListItem, APaginationNext, APaginationPrev, APaginationRoot } from 'akar';
const btnClasses = 'text-sm color-text font-500 p-1.5 rounded-md bg-background inline-flex gap-1.5 ring ring-ring-accented ring-inset transition-colors-280 items-center focus:outline-none active:bg-background-elevated aria-disabled:(bg-background opacity-75 cursor-not-allowed) disabled:(bg-background opacity-75 cursor-not-allowed) hover:bg-background-elevated focus-visible:(ring-2 ring-ring-inverted)';
</script>
<template>
<APaginationRoot
:total="100"
:sibling-count="1"
:items-per-page="10"
show-edges
:default-page="2"
>
<APaginationList
v-slot="{ items }"
class="flex gap-1 items-center"
>
<APaginationFirst :class="btnClasses">
<i class="i-lucide:chevrons-left size-5" />
</APaginationFirst>
<APaginationPrev :class="btnClasses">
<i class="i-lucide:chevron-left size-5" />
</APaginationPrev>
<template v-for="(page, index) in items">
<APaginationListItem
v-if="page.type === 'page'"
:key="index"
class="data-[selected]:bg-primary"
:class="btnClasses"
:value="page.value"
>
<span class="min-w-5">{{ page.value }}</span>
</APaginationListItem>
<APaginationEllipsis
v-else
:key="page.type"
:index="index"
:class="btnClasses"
>
<i class="i-lucide:ellipsis size-5" />
</APaginationEllipsis>
</template>
<APaginationNext :class="btnClasses">
<i class="i-lucide:chevron-right size-5" />
</APaginationNext>
<APaginationLast :class="btnClasses">
<i class="i-lucide:chevrons-right size-5" />
</APaginationLast>
</APaginationList>
</APaginationRoot>
</template>
Import all parts and piece them together.
<script setup>
import { APaginationEllipsis, APaginationFirst, APaginationLast, APaginationList, APaginationListItem, APaginationNext, APaginationPrev, APaginationRoot } from 'akar';
</script>
<template>
<APaginationRoot>
<APaginationList v-slot="{ items }">
<APaginationFirst />
<APaginationPrev />
<template v-for="(page, index) in items">
<APaginationListItem
v-if="page.type === 'page'"
:key="index"
/>
<APaginationEllipsis
v-else
:key="page.type"
:index="index"
>
…
</APaginationEllipsis>
</template>
<APaginationNext />
<APaginationLast />
</APaginationList>
</APaginationRoot>
</template>
One benefit of using Akar is its flexibility and low-level control over the components. However, this also means that you may need to manually construct more complex UI elements by combining multiple Akar components together.
If you feel there's a lot of elements that needs to be constructed manually using Akar, consider using Pohon UI instead. It provides a higher-level abstraction over Akar components with pre-defined styles and behaviors that can help you build UIs faster.
Contains all of the paginations parts.
| Prop | Default | Type |
|---|---|---|
as | 'nav' | APrimitiveAsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
defaultPage | 1 | numberThe value of the page that should be active when initially rendered. Use when you do not need to control the value state. |
disabled | booleanWhen | |
itemsPerPage* | numberNumber of items per page | |
page | number | |
showEdges | false | booleanWhen |
siblingCount | 2 | numberNumber of sibling should be shown around the current page |
total | 0 | numberNumber of items in your list |
| Event | Type |
|---|---|
update:page | [value: number]Event handler called when the page value changes |
| Slot | Type |
|---|---|
page | numberThe controlled value of the current page. Can be binded as |
pageCount | numberNumber of pages |
Used to show the list of pages. It also makes pagination accessible to assistive technologies.
| Prop | Default | Type |
|---|---|---|
as | 'div' | APrimitiveAsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
| Slot | Type |
|---|---|
items | { type: 'ellipsis'; } | { type: 'page'; value: number; }Pages item |
Used to render the button that changes the current page.
| Attribute | Value |
|---|---|
[data-selected] | 'true' | '' |
[data-type] | 'page' |
Placeholder element when the list is long, and only a small amount of siblingCount was set and showEdges was set to true.
| Prop | Default | Type |
|---|---|---|
as | 'div' | APrimitiveAsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
| Attribute | Value |
|---|---|
[data-type] | 'ellipsis' |
Triggers that set the page value to 1
| Prop | Default | Type |
|---|---|---|
as | 'button' | APrimitiveAsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Triggers that set the page value to the previous page
| Prop | Default | Type |
|---|---|---|
as | 'button' | APrimitiveAsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Triggers that set the page value to the next page
| Prop | Default | Type |
|---|---|---|
as | 'button' | APrimitiveAsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Triggers that set the page value to the last page
| Prop | Default | Type |
|---|---|---|
as | 'button' | APrimitiveAsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
You can add APaginationEllipsis as a visual cue for more previous and after items.
<script setup lang="ts">
import { APaginationEllipsis, APaginationList, APaginationListItem, APaginationRoot } from 'akar';
</script>
<template>
<APaginationRoot>
<APaginationList v-slot="{ items }">
<template v-for="(page, index) in items">
<APaginationListItem
v-if="page.type === 'page'"
:key="index"
/>
<APaginationEllipsis
v-else
:key="page.type"
:index="index"
>
…
</APaginationEllipsis>
</template>
</APaginationList>
</APaginationRoot>
</template>
You can add APaginationFirst to allow user to navigate to first page, or APaginationLast to navigate to last page.
<script setup lang="ts">
import { APaginationFirst, APaginationLast, APaginationList, APaginationListItem, APaginationRoot } from 'akar';
</script>
<template>
<APaginationRoot>
<APaginationList>
<APaginationFirst />
...
<APaginationLast />
</APaginationList>
</APaginationRoot>
</template>
You can control the current page by passing it a reactive value.
<script setup lang="ts">
import { APaginationRoot } from 'akar';
import { ref } from 'vue';
import { Select } from './custom-select';
const currentPage = ref(1);
</script>
<template>
<Select v-model="currentPage" />
<APaginationRoot v-model:page="currentPage">
...
</APaginationRoot>
</template>
| Key | Description |
|---|---|
Tab | Moves focus to the next focusable element. |
Space | When focus is on a any trigger, trigger selected page or arrow navigation |
Enter | When focus is on a any trigger, trigger selected page or arrow navigation |