Skip to content
edmi-ui

Pagination

Page navigation with the current page clearly marked and long ranges collapsed with an ellipsis.

StackBlitz ↗StackBlitz ↗StackBlitz ↗
Terminal window
npx shadcn@latest add @edmi-ui/pagination
Terminal window
pnpm dlx shadcn@latest add @edmi-ui/pagination
Terminal window
yarn dlx shadcn@latest add @edmi-ui/pagination
Terminal window
bunx shadcn@latest add @edmi-ui/pagination

Pick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.

Terminal window
npx shadcn-vue@latest add @edmi-ui/pagination
Terminal window
pnpm dlx shadcn-vue@latest add @edmi-ui/pagination
Terminal window
yarn dlx shadcn-vue@latest add @edmi-ui/pagination
Terminal window
bunx shadcn-vue@latest add @edmi-ui/pagination

Pick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.

Terminal window
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/pagination.json
Terminal window
pnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/pagination.json
Terminal window
yarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/pagination.json
Terminal window
bunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/pagination.json

Pick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.

import {
Pagination,
PaginationContent,
PaginationItem,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href="#"
/>
</PaginationItem>
<PaginationItem>
<PaginationNext
href="#"
/>
</PaginationItem>
</PaginationContent>
</Pagination>
<script setup lang="ts">
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";
</script>
<template>
<Pagination v-slot="{ page }" :total="100" :items-per-page="10" :default-page="4" :sibling-count="1" show-edges>
<PaginationContent v-slot="{ items }">
<PaginationPrevious />
<template v-for="(item, index) in items" :key="index">
<PaginationItem v-if="item.type === 'page'" :value="item.value" :is-active="item.value === page">
{{ item.value }}
</PaginationItem>
<PaginationEllipsis v-else :index="index" />
</template>
<PaginationNext />
</PaginationContent>
</Pagination>
</template>
<script lang="ts">
import * as Pagination from "$lib/components/ui/pagination";
</script>
<Pagination.Root count={100} perPage={10} page={4}>
{#snippet children({ pages, currentPage })}
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous />
</Pagination.Item>
{#each pages as page (page.key)}
{#if page.type === "ellipsis"}
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
{:else}
<Pagination.Item>
<Pagination.Link {page} isActive={currentPage === page.value}>
{page.value}
</Pagination.Link>
</Pagination.Item>
{/if}
{/each}
<Pagination.Item>
<Pagination.Next />
</Pagination.Item>
</Pagination.Content>
{/snippet}
</Pagination.Root>
registry/ui/pagination.tsx
"use client";
import { cn } from "cn";
import * as React from "react";
import { CaretLeftIcon, CaretRightIcon, DotsThreeIcon } from "@phosphor-icons/react";
import { Button } from "@/registry/edmi/ui/button";
import { type Elevation, useElevation } from "@/registry/edmi/ui/elevation";
// ✦ `elevation` on Pagination flows to the active PaginationLink (only the active link rises).
const PaginationContext = React.createContext<{ raised: boolean }>({
raised: false,
});
function Pagination({
className,
elevation,
...props
}: React.ComponentProps<"nav"> & {
/** ✦ depth: raised +1 / floating +2 make the active page link rise. */
elevation?: Elevation;
}) {
const level = useElevation(elevation, "control");
return (
<PaginationContext.Provider
value={{ raised: level === "raised" || level === "floating" }}
>
<nav
aria-label="pagination"
data-slot="pagination"
className={cn("mx-auto flex w-full justify-center", className)}
{...props}
/>
</PaginationContext.Provider>
);
}
function PaginationContent({
className,
...props
}: React.ComponentProps<"ul">) {
return (
<ul
data-slot="pagination-content"
className={cn("flex items-center gap-0.5", className)}
{...props}
/>
);
}
function PaginationItem({ ...props }: React.ComponentProps<"li">) {
return <li data-slot="pagination-item" {...props} />;
}
type PaginationLinkProps = {
isActive?: boolean;
/** ✦ depth of this link (the active link rises); defaults to the Pagination level. */
elevation?: Elevation;
} & Pick<React.ComponentProps<typeof Button>, "size"> &
React.ComponentProps<"a">;
function PaginationLink({
className,
isActive,
elevation,
size = "icon",
...props
}: PaginationLinkProps) {
const context = React.useContext(PaginationContext);
const own =
elevation && elevation !== "auto"
? elevation === "raised" || elevation === "floating"
: undefined;
return (
<Button
variant={isActive ? "outline" : "ghost"}
size={size}
className={cn(
isActive && "border-input bg-card font-semibold",
isActive &&
(own ?? context.raised) &&
"border-transparent shadow-btn-raised-neutral",
className,
)}
nativeButton={false}
render={
<a
aria-current={isActive ? "page" : undefined}
data-slot="pagination-link"
data-active={isActive}
{...props}
/>
}
/>
);
}
function PaginationPrevious({
className,
text = "Previous",
...props
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
return (
<PaginationLink
aria-label="Go to previous page"
size="default"
className={cn("pl-1.5!", className)}
{...props}
>
<CaretLeftIcon
data-icon="inline-start"
className="rtl:rotate-180"
/>
<span className="hidden sm:block">{text}</span>
</PaginationLink>
);
}
function PaginationNext({
className,
text = "Next",
...props
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
return (
<PaginationLink
aria-label="Go to next page"
size="default"
className={cn("pr-1.5!", className)}
{...props}
>
<span className="hidden sm:block">{text}</span>
<CaretRightIcon
data-icon="inline-end"
className="rtl:rotate-180"
/>
</PaginationLink>
);
}
function PaginationEllipsis({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
aria-hidden
data-slot="pagination-ellipsis"
className={cn(
"flex size-8 items-center justify-center [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
>
<DotsThreeIcon
/>
<span className="sr-only">More pages</span>
</span>
);
}
export {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
};
registry/ui/pagination/Pagination.vue
<script setup lang="ts">
import type { PaginationRootEmits, PaginationRootProps } from "reka-ui"
import type { HTMLAttributes } from "vue"
import { reactiveOmit } from "@vueuse/core"
import { PaginationRoot, useForwardPropsEmits } from "reka-ui"
import { computed, provide } from "vue"
import { cn } from "@/registry/edmi/lib/utils"
import { type Elevation, useElevation } from "@/registry/edmi/ui/elevation"
const props = withDefaults(defineProps<PaginationRootProps & {
class?: HTMLAttributes["class"]
/** ✦ depth: raised +1 / floating +2 make the active page link rise */
elevation?: Elevation
}>(), { elevation: undefined })
const level = useElevation(() => props.elevation, "control")
const raised = computed(() => level.value === "raised" || level.value === "floating")
provide("pagination", {
get raised() { return raised.value },
})
const emits = defineEmits<PaginationRootEmits>()
const delegatedProps = reactiveOmit(props, "class", "elevation")
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<PaginationRoot
v-slot="slotProps"
data-slot="pagination"
v-bind="forwarded"
:class="cn('mx-auto flex w-full justify-center', props.class)"
>
<slot v-bind="slotProps" />
</PaginationRoot>
</template>
registry/ui/pagination/PaginationContent.vue
<script setup lang="ts">
import type { PaginationListProps } from "reka-ui"
import type { HTMLAttributes } from "vue"
import { reactiveOmit } from "@vueuse/core"
import { PaginationList } from "reka-ui"
import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<PaginationListProps & { class?: HTMLAttributes["class"] }>()
const delegatedProps = reactiveOmit(props, "class")
</script>
<template>
<PaginationList
v-slot="slotProps"
data-slot="pagination-content"
v-bind="delegatedProps"
:class="cn('flex items-center gap-0.5', props.class)"
>
<slot v-bind="slotProps" />
</PaginationList>
</template>
registry/ui/pagination/PaginationEllipsis.vue
<script setup lang="ts">
import type { PaginationEllipsisProps } from "reka-ui"
import type { HTMLAttributes } from "vue"
import { MoreHorizontal } from "@lucide/vue"
import { reactiveOmit } from "@vueuse/core"
import { PaginationEllipsis } from "reka-ui"
import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<PaginationEllipsisProps & { class?: HTMLAttributes["class"] }>()
const delegatedProps = reactiveOmit(props, "class")
</script>
<template>
<PaginationEllipsis
data-slot="pagination-ellipsis"
v-bind="delegatedProps"
:class="cn('flex size-8 items-center justify-center [&_svg:not([class*=size-])]:size-4', props.class)"
>
<slot>
<MoreHorizontal />
<span class="sr-only">More pages</span>
</slot>
</PaginationEllipsis>
</template>
registry/ui/pagination/PaginationFirst.vue
<script setup lang="ts">
import type { PaginationFirstProps } from "reka-ui"
import type { HTMLAttributes } from "vue"
import type { ButtonVariants } from "@/registry/edmi/ui/button"
import { ChevronLeftIcon } from "@lucide/vue"
import { reactiveOmit } from "@vueuse/core"
import { PaginationFirst, useForwardProps } from "reka-ui"
import { cn } from "@/registry/edmi/lib/utils"
import { buttonVariants } from "@/registry/edmi/ui/button"
const props = withDefaults(defineProps<PaginationFirstProps & {
size?: ButtonVariants["size"]
class?: HTMLAttributes["class"]
}>(), {
size: "default",
})
const delegatedProps = reactiveOmit(props, "class", "size")
const forwarded = useForwardProps(delegatedProps)
</script>
<template>
<PaginationFirst
data-slot="pagination-first"
:class="cn(buttonVariants({ variant: 'ghost', size }), 'pl-1.5!', props.class)"
v-bind="forwarded"
>
<slot>
<ChevronLeftIcon data-icon="inline-start" class="rtl:rotate-180" />
<span class="hidden sm:block">First</span>
</slot>
</PaginationFirst>
</template>
registry/ui/pagination/PaginationItem.vue
<script setup lang="ts">
import type { PaginationListItemProps } from "reka-ui"
import type { HTMLAttributes } from "vue"
import type { ButtonVariants } from "@/registry/edmi/ui/button"
import { reactiveOmit } from "@vueuse/core"
import { PaginationListItem } from "reka-ui"
import { computed, inject } from "vue"
import { cn } from "@/registry/edmi/lib/utils"
import type { Elevation } from "@/registry/edmi/ui/elevation"
import { buttonVariants } from "@/registry/edmi/ui/button"
const props = withDefaults(defineProps<PaginationListItemProps & {
size?: ButtonVariants["size"]
class?: HTMLAttributes["class"]
isActive?: boolean
/** ✦ overrides the Pagination `elevation` for this link (the active link rises) */
elevation?: Elevation
}>(), {
size: "icon",
elevation: undefined,
})
const context = inject<{ raised?: boolean } | null>("pagination", null)
const delegatedProps = reactiveOmit(props, "class", "size", "isActive", "elevation")
const raised = computed(() =>
props.elevation && props.elevation !== "auto"
? props.elevation === "raised" || props.elevation === "floating"
: !!context?.raised,
)
</script>
<template>
<PaginationListItem
data-slot="pagination-item"
v-bind="delegatedProps"
:class="cn(
buttonVariants({
variant: isActive ? 'outline' : 'ghost',
size,
}),
isActive && 'bg-card font-semibold',
isActive && raised && 'border-transparent shadow-btn-raised-neutral',
props.class)"
>
<slot />
</PaginationListItem>
</template>
registry/ui/pagination/PaginationLast.vue
<script setup lang="ts">
import type { PaginationLastProps } from "reka-ui"
import type { HTMLAttributes } from "vue"
import type { ButtonVariants } from "@/registry/edmi/ui/button"
import { ChevronRightIcon } from "@lucide/vue"
import { reactiveOmit } from "@vueuse/core"
import { PaginationLast, useForwardProps } from "reka-ui"
import { cn } from "@/registry/edmi/lib/utils"
import { buttonVariants } from "@/registry/edmi/ui/button"
const props = withDefaults(defineProps<PaginationLastProps & {
size?: ButtonVariants["size"]
class?: HTMLAttributes["class"]
}>(), {
size: "default",
})
const delegatedProps = reactiveOmit(props, "class", "size")
const forwarded = useForwardProps(delegatedProps)
</script>
<template>
<PaginationLast
data-slot="pagination-last"
:class="cn(buttonVariants({ variant: 'ghost', size }), 'pr-1.5!', props.class)"
v-bind="forwarded"
>
<slot>
<span class="hidden sm:block">Last</span>
<ChevronRightIcon data-icon="inline-end" class="rtl:rotate-180" />
</slot>
</PaginationLast>
</template>
registry/ui/pagination/PaginationNext.vue
<script setup lang="ts">
import type { PaginationNextProps } from "reka-ui"
import type { HTMLAttributes } from "vue"
import type { ButtonVariants } from "@/registry/edmi/ui/button"
import { ChevronRightIcon } from "@lucide/vue"
import { reactiveOmit } from "@vueuse/core"
import { PaginationNext, useForwardProps } from "reka-ui"
import { cn } from "@/registry/edmi/lib/utils"
import { buttonVariants } from "@/registry/edmi/ui/button"
const props = withDefaults(defineProps<PaginationNextProps & {
size?: ButtonVariants["size"]
class?: HTMLAttributes["class"]
}>(), {
size: "default",
})
const delegatedProps = reactiveOmit(props, "class", "size")
const forwarded = useForwardProps(delegatedProps)
</script>
<template>
<PaginationNext
data-slot="pagination-next"
:class="cn(buttonVariants({ variant: 'ghost', size }), 'pr-1.5!', props.class)"
v-bind="forwarded"
>
<slot>
<span class="hidden sm:block">Next</span>
<ChevronRightIcon data-icon="inline-end" class="rtl:rotate-180" />
</slot>
</PaginationNext>
</template>
registry/ui/pagination/PaginationPrevious.vue
<script setup lang="ts">
import type { PaginationPrevProps } from "reka-ui"
import type { HTMLAttributes } from "vue"
import type { ButtonVariants } from "@/registry/edmi/ui/button"
import { ChevronLeftIcon } from "@lucide/vue"
import { reactiveOmit } from "@vueuse/core"
import { PaginationPrev, useForwardProps } from "reka-ui"
import { cn } from "@/registry/edmi/lib/utils"
import { buttonVariants } from "@/registry/edmi/ui/button"
const props = withDefaults(defineProps<PaginationPrevProps & {
size?: ButtonVariants["size"]
class?: HTMLAttributes["class"]
}>(), {
size: "default",
})
const delegatedProps = reactiveOmit(props, "class", "size")
const forwarded = useForwardProps(delegatedProps)
</script>
<template>
<PaginationPrev
data-slot="pagination-previous"
:class="cn(buttonVariants({ variant: 'ghost', size }), 'pl-1.5!', props.class)"
v-bind="forwarded"
>
<slot>
<ChevronLeftIcon data-icon="inline-start" class="rtl:rotate-180" />
<span class="hidden sm:block">Previous</span>
</slot>
</PaginationPrev>
</template>
registry/ui/pagination/index.ts
export { default as Pagination } from "./Pagination.vue";
export { default as PaginationContent } from "./PaginationContent.vue";
export { default as PaginationEllipsis } from "./PaginationEllipsis.vue";
export { default as PaginationFirst } from "./PaginationFirst.vue";
export { default as PaginationItem } from "./PaginationItem.vue";
export { default as PaginationLast } from "./PaginationLast.vue";
export { default as PaginationNext } from "./PaginationNext.vue";
export { default as PaginationPrevious } from "./PaginationPrevious.vue";
src/lib/registry/ui/pagination/pagination-content.svelte
<script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLUListElement>> = $props();
</script>
<ul
bind:this={ref}
data-slot="pagination-content"
class={cn(
"flex items-center gap-0.5", className)}
{...restProps}
>
{@render children?.()}
</ul>
src/lib/registry/ui/pagination/pagination-ellipsis.svelte
<script lang="ts">
import DotsThreeIcon from "phosphor-svelte/lib/DotsThree";
import { cn, type WithElementRef, type WithoutChildren } from "$lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
...restProps
}: WithoutChildren<WithElementRef<HTMLAttributes<HTMLSpanElement>>> = $props();
</script>
<span
bind:this={ref}
aria-hidden="true"
data-slot="pagination-ellipsis"
class={cn(
"flex size-8 items-center justify-center [&_svg:not([class*='size-'])]:size-4", className)}
{...restProps}
>
<DotsThreeIcon
/>
<span class="sr-only">More pages</span>
</span>
src/lib/registry/ui/pagination/pagination-item.svelte
<script lang="ts">
import type { WithElementRef } from "$lib/utils.js";
import type { HTMLLiAttributes } from "svelte/elements";
let {
ref = $bindable(null),
children,
...restProps
}: WithElementRef<HTMLLiAttributes> = $props();
</script>
<li bind:this={ref} data-slot="pagination-item" {...restProps}>
{@render children?.()}
</li>
src/lib/registry/ui/pagination/pagination-link.svelte
<script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui";
import { buttonVariants, type ButtonSize } from "$lib/registry/ui/button/index.js";
import { cn } from "$lib/utils.js";
import type { Elevation } from "$lib/registry/ui/elevation/index.js";
import { getPaginationCtx } from "./pagination.svelte";
let {
ref = $bindable(null),
class: className,
size = "icon",
isActive,
page,
elevation = "auto",
children,
...restProps
}: PaginationPrimitive.PageProps & {
size?: ButtonSize;
isActive: boolean;
/** ✦ overrides the Pagination `elevation` (the active link rises). */
elevation?: Elevation;
} = $props();
const ctx = getPaginationCtx();
const isRaised = $derived(
elevation !== "auto" ? elevation === "raised" || elevation === "floating" : (ctx?.raised ?? false)
);
</script>
{#snippet Fallback()}
{page.value}
{/snippet}
<PaginationPrimitive.Page
bind:ref
{page}
aria-current={isActive ? "page" : undefined}
data-slot="pagination-link"
data-active={isActive}
data-size={size}
class={cn(
buttonVariants({ size, variant: isActive ? "outline" : "ghost" }),
// paginationActive: flat outline, elevation ✦ raises it
isActive && "bg-card font-semibold",
isActive && isRaised && "border-transparent shadow-btn-raised-neutral",
className
)}
{...restProps}
>
{#if children}
{@render children?.()}
{:else}
{@render Fallback()}
{/if}
</PaginationPrimitive.Page>
src/lib/registry/ui/pagination/pagination-next-button.svelte
<script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui";
import CaretRightIcon from "phosphor-svelte/lib/CaretRight";
import { cn } from "$lib/utils.js";
import { buttonVariants } from "../button/index.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: PaginationPrimitive.NextButtonProps = $props();
</script>
{#snippet Fallback()}
<span>Next</span>
<CaretRightIcon
class={cn("rtl:rotate-180 size-4", className)}
/>
{/snippet}
<PaginationPrimitive.NextButton
bind:ref
aria-label="Go to next page"
class={cn(buttonVariants({ variant: "ghost" }), "pr-1.5!", className)}
{...restProps}
>
{#if children}
{@render children?.()}
{:else}
{@render Fallback()}
{/if}
</PaginationPrimitive.NextButton>
src/lib/registry/ui/pagination/pagination-next.svelte
<script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui";
import CaretRightIcon from "phosphor-svelte/lib/CaretRight";
import { buttonVariants } from "$lib/registry/ui/button/index.js";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: PaginationPrimitive.NextButtonProps = $props();
</script>
<PaginationPrimitive.NextButton
bind:ref
aria-label="Go to next page"
class={cn(buttonVariants({ variant: "ghost", size: "default" }), "pr-1.5!", className)}
{...restProps}
>
<span class="hidden sm:block">Next</span>
<CaretRightIcon
data-icon="inline-end"
class="rtl:rotate-180"
/>
</PaginationPrimitive.NextButton>
src/lib/registry/ui/pagination/pagination-prev-button.svelte
<script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui";
import CaretLeftIcon from "phosphor-svelte/lib/CaretLeft";
import { cn } from "$lib/utils.js";
import { buttonVariants } from "$lib/registry/ui/button/index.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: PaginationPrimitive.PrevButtonProps = $props();
</script>
{#snippet Fallback()}
<CaretLeftIcon
class={cn("rtl:rotate-180 size-4", className)}
/>
<span>Previous</span>
{/snippet}
<PaginationPrimitive.PrevButton
bind:ref
aria-label="Go to previous page"
class={cn(buttonVariants({ variant: "ghost" }), "pl-1.5!", className)}
{...restProps}
>
{#if children}
{@render children?.()}
{:else}
{@render Fallback()}
{/if}
</PaginationPrimitive.PrevButton>
src/lib/registry/ui/pagination/pagination-previous.svelte
<script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui";
import CaretLeftIcon from "phosphor-svelte/lib/CaretLeft";
import { buttonVariants } from "$lib/registry/ui/button/index.js";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: PaginationPrimitive.PrevButtonProps = $props();
</script>
<PaginationPrimitive.PrevButton
bind:ref
aria-label="Go to previous page"
class={cn(
buttonVariants({ variant: "ghost", size: "default" }),
"pl-1.5!",
className
)}
{...restProps}
>
<CaretLeftIcon
data-icon="inline-start"
class="rtl:rotate-180"
/>
<span class="hidden sm:block">Previous</span>
</PaginationPrimitive.PrevButton>
src/lib/registry/ui/pagination/pagination.svelte
<script lang="ts" module>
import { getContext, setContext } from "svelte";
interface PaginationContext {
raised: boolean;
}
export function setPaginationCtx(ctx: PaginationContext) {
setContext("pagination", ctx);
}
export function getPaginationCtx() {
return getContext<PaginationContext | undefined>("pagination");
}
</script>
<script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
import { type Elevation, useElevation } from "$lib/registry/ui/elevation/index.js";
let {
ref = $bindable(null),
class: className,
count = 0,
perPage = 10,
page = $bindable(1),
siblingCount = 1,
elevation = "auto",
...restProps
}: PaginationPrimitive.RootProps & {
/** ✦ depth: raised +1 / floating +2 make the active link rise. */
elevation?: Elevation;
} = $props();
const level = useElevation(() => elevation, "control");
const raised = $derived(level.current === "raised" || level.current === "floating");
setPaginationCtx({
get raised() {
return raised;
},
});
</script>
<PaginationPrimitive.Root
bind:ref
bind:page
role="navigation"
aria-label="pagination"
data-slot="pagination"
{count}
{perPage}
{siblingCount}
class={cn("mx-auto flex w-full justify-center", className)}
{...restProps}
/>
src/lib/registry/ui/pagination/index.ts
import Root from "./pagination.svelte";
import Content from "./pagination-content.svelte";
import Ellipsis from "./pagination-ellipsis.svelte";
import Item from "./pagination-item.svelte";
import Link from "./pagination-link.svelte";
import Next from "./pagination-next.svelte";
import NextButton from "./pagination-next-button.svelte";
import PrevButton from "./pagination-prev-button.svelte";
import Previous from "./pagination-previous.svelte";
export {
Content,
Content as PaginationContent,
Ellipsis,
Ellipsis as PaginationEllipsis,
Item,
Item as PaginationItem,
Link,
Link as PaginationLink,
Next,
Next as PaginationNext,
NextButton, // old
NextButton as PaginationNextButton, // old
PrevButton, // old
PrevButton as PaginationPrevButton, // old
Previous,
Previous as PaginationPrevious,
Root,
//
Root as Pagination,
};

See the Elevation guide for the levels, roles and layered mode.

Flat by default. Set raised on Pagination: only the active link rises (bevel); the list never does. PaginationLink can override it.

StackBlitz ↗StackBlitz ↗StackBlitz ↗

Flat (0)

Raised (+1): only the active link rises

Flat (0)

Raised (+1): only the active link rises

Flat (0)

Raised (+1): only the active link rises

Prop Type Default
PaginationLink isActive boolean false
PaginationLink size Button sizes icon
PaginationPrevious text / PaginationNext text string Previous / Next
elevation "auto" | "sunken" | "flat" | "raised" | "floating" "auto"

Parts: Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis.

  • Links are Edmi Buttons: active page is outline, others ghost.