<script lang="ts">
import * as Pagination from "$lib/components/ui/pagination/index.js";
</script>
<Pagination.Root count={30} page={2}>
{#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.Ellipsis />
</Pagination.Item>
<Pagination.Item>
<Pagination.Next />
</Pagination.Item>
</Pagination.Content>
{/snippet}
</Pagination.Root> Installation
pnpm dlx shadcn-svelte@latest add pagination Install bits-ui:
pnpm add bits-ui -D Copy and paste the following code into your project.
import Root from './pagination.svelte';
import Content from './pagination-content.svelte';
import Item from './pagination-item.svelte';
import Link from './pagination-link.svelte';
import PrevButton from './pagination-prev-button.svelte';
import NextButton from './pagination-next-button.svelte';
import Ellipsis from './pagination-ellipsis.svelte';
import Previous from './pagination-previous.svelte';
import Next from './pagination-next.svelte';
export {
Root,
Content,
Item,
Link,
PrevButton, // old
NextButton, // old
Ellipsis,
Previous,
Next,
//
Root as Pagination,
Content as PaginationContent,
Item as PaginationItem,
Link as PaginationLink,
PrevButton as PaginationPrevButton, // old
NextButton as PaginationNextButton, // old
Ellipsis as PaginationEllipsis,
Previous as PaginationPrevious,
Next as PaginationNext
};
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$UTILS$.js';
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-1 rounded-full border border-zinc-800 bg-background p-1',
className
)}
{...restProps}
>
{@render children?.()}
</ul>
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef, type WithoutChildren } from '$UTILS$.js';
import DotsThreeIcon from 'phosphor-svelte/lib/DotsThree';
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-9 items-center justify-center rounded-full text-zinc-500 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...restProps}
>
<DotsThreeIcon />
<span class="sr-only">More pages</span>
</span>
<script lang="ts">
import type { HTMLLiAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$UTILS$.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLLiAttributes> = $props();
</script>
<li
bind:this={ref}
data-slot="pagination-item"
class={cn('flex items-center', className)}
{...restProps}
>
{@render children?.()}
</li>
<script lang="ts">
import { Pagination as PaginationPrimitive } from 'bits-ui';
import { cn } from '$UTILS$.js';
import { buttonVariants, type ButtonSize } from '$UI$/button/index.js';
let {
ref = $bindable(null),
class: className,
size = 'icon',
isActive,
page,
children,
...restProps
}: PaginationPrimitive.PageProps & {
size?: ButtonSize;
isActive: boolean;
} = $props();
</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' }),
'cn-pagination-link rounded-full border-zinc-800 font-mono text-xs font-semibold text-zinc-400 data-active:border-[#d0e891] data-active:bg-[#b9d765] data-active:text-[#101207] hover:bg-zinc-950 hover:text-white',
className
)}
{...restProps}
>
{#if children}
{@render children?.()}
{:else}
{@render Fallback()}
{/if}
</PaginationPrimitive.Page>
<script lang="ts">
import { Pagination as PaginationPrimitive } from 'bits-ui';
import CaretRightIcon from 'phosphor-svelte/lib/CaretRight';
import { cn } from '$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('size-4', className)} />
{/snippet}
<PaginationPrimitive.NextButton
bind:ref
aria-label="Go to next page"
class={cn(
buttonVariants({ variant: 'ghost' }),
'rounded-full border border-zinc-800 bg-background pr-2! font-mono text-xs font-semibold tracking-[0.08em] text-zinc-400 uppercase hover:border-[#d0e891] hover:bg-[#b9d765] hover:text-[#101207]',
className
)}
{...restProps}
>
{#if children}
{@render children?.()}
{:else}
{@render Fallback()}
{/if}
</PaginationPrimitive.NextButton>
<script lang="ts">
import { Pagination as PaginationPrimitive } from 'bits-ui';
import { cn } from '$UTILS$.js';
import { buttonVariants } from '$UI$/button/index.js';
import CaretRightIcon from 'phosphor-svelte/lib/CaretRight';
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' }),
'rounded-full border border-zinc-800 bg-background pr-2! font-mono text-xs font-semibold tracking-[0.08em] text-zinc-400 uppercase hover:border-[#d0e891] hover:bg-[#b9d765] hover:text-[#101207]',
className
)}
{...restProps}
>
<span class="cn-pagination-next-text hidden sm:block">Next</span>
<CaretRightIcon data-icon="inline-end" />
</PaginationPrimitive.NextButton>
<script lang="ts">
import { Pagination as PaginationPrimitive } from 'bits-ui';
import CaretLeftIcon from 'phosphor-svelte/lib/CaretLeft';
import { cn } from '$UTILS$.js';
import { buttonVariants } from '../button/index.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: PaginationPrimitive.PrevButtonProps = $props();
</script>
{#snippet Fallback()}
<CaretLeftIcon class={cn('size-4', className)} />
<span>Previous</span>
{/snippet}
<PaginationPrimitive.PrevButton
bind:ref
aria-label="Go to previous page"
class={cn(
buttonVariants({ variant: 'ghost' }),
'rounded-full border border-zinc-800 bg-background pl-2! font-mono text-xs font-semibold tracking-[0.08em] text-zinc-400 uppercase hover:border-[#d0e891] hover:bg-[#b9d765] hover:text-[#101207]',
className
)}
{...restProps}
>
{#if children}
{@render children?.()}
{:else}
{@render Fallback()}
{/if}
</PaginationPrimitive.PrevButton>
<script lang="ts">
import { Pagination as PaginationPrimitive } from 'bits-ui';
import { cn } from '$UTILS$.js';
import { buttonVariants } from '$UI$/button/index.js';
import CaretLeftIcon from 'phosphor-svelte/lib/CaretLeft';
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' }),
'rounded-full border border-zinc-800 bg-background pl-2! font-mono text-xs font-semibold tracking-[0.08em] text-zinc-400 uppercase hover:border-[#d0e891] hover:bg-[#b9d765] hover:text-[#101207]',
className
)}
{...restProps}
>
<CaretLeftIcon data-icon="inline-start" />
<span class="cn-pagination-previous-text hidden sm:block">Previous</span>
</PaginationPrimitive.PrevButton>
<script lang="ts">
import { Pagination as PaginationPrimitive } from 'bits-ui';
import { cn } from '$UTILS$.js';
let {
ref = $bindable(null),
class: className,
count = 0,
perPage = 10,
page = $bindable(1),
siblingCount = 1,
...restProps
}: PaginationPrimitive.RootProps = $props();
</script>
<PaginationPrimitive.Root
bind:ref
bind:page
role="navigation"
aria-label="pagination"
data-slot="pagination"
{count}
{perPage}
{siblingCount}
class={cn('cn-pagination mx-auto flex w-full justify-center font-mono text-xs', className)}
{...restProps}
/>
Usage
<script lang="ts">
import * as Pagination from '$lib/components/ui/pagination/index.js';
</script> <Pagination.Root count={100} perPage={10}>
{#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>