Pagination
A navigation component for moving between pages of content. Built on Ark UI’s Pagination — supports controlled/uncontrolled modes, custom page sizes, and link-based navigation.
DocsBasic
Basic pagination (100 items, 10 per page)
import {
Pagination,
PaginationPageList,
PaginationFirstTrigger,
PaginationPrevTrigger,
PaginationNextTrigger,
PaginationLastTrigger,
} from "~/components/pagination";
export default function PaginationBasicDemo() {
return (
<div>
<div>
<p class="text-sm text-muted-foreground mb-3">Basic pagination (100 items, 10 per page)</p>
<Pagination count={100} pageSize={10} class="gap-1">
<PaginationFirstTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="11 17 6 12 11 7" />
<polyline points="18 17 13 12 18 7" />
</svg>
</PaginationFirstTrigger>
<PaginationPrevTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6" />
</svg>
</PaginationPrevTrigger>
<PaginationPageList />
<PaginationNextTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6" />
</svg>
</PaginationNextTrigger>
<PaginationLastTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="13 17 18 12 13 7" />
<polyline points="6 17 11 12 6 7" />
</svg>
</PaginationLastTrigger>
</Pagination>
</div>
</div>
);
}Installation
CLI
Run the following command to add the component to your project:
npx @ark-preset/cli@latest add paginationManual
Create the recipe file at src/components/recipes/pagination.ts:
import { tv, type VariantProps } from "tailwind-variants";
export const paginationVariants = tv({
slots: {
root: "mx-auto flex w-full justify-center",
trigger:
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition duration-300 motion-safe:hover:-translate-y-0.5 motion-safe:active:translate-y-0.5 active:duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground h-8 min-w-8",
item: [
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition duration-300 motion-safe:hover:-translate-y-0.5 motion-safe:active:translate-y-0.5 active:duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground",
"h-8 min-w-8",
"data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-foreground",
],
ellipsis: "flex h-8 min-w-8 items-center justify-center text-sm",
},
variants: {
size: {
sm: {
item: "h-7 min-w-7",
ellipsis: "h-7 min-w-7",
},
md: {
item: "h-8 min-w-8",
ellipsis: "h-8 min-w-8",
},
lg: {
item: "h-9 min-w-9",
ellipsis: "h-9 min-w-9",
},
},
},
defaultVariants: {
size: "md",
},
});
export type PaginationVariants = VariantProps<typeof paginationVariants>;Create the component directory and files.
src/components/pagination/pagination.base.tsx:
import { Pagination as ArkPagination } from "@ark-ui/solid/pagination";
import { createContext, useContext, splitProps, type Component } from "solid-js";
import { paginationVariants, type PaginationVariants } from "../recipes/pagination";
type PaginationVariantContextValue = Pick<PaginationVariants, "size">;
const PaginationVariantContext = createContext<PaginationVariantContextValue>();
const usePaginationVariant = () => useContext(PaginationVariantContext);
const styles = paginationVariants();
const Root: Component<ArkPagination.RootProps & PaginationVariants> = (props) => {
const [local, others] = splitProps(props, ["class", "size"]);
return (
<PaginationVariantContext.Provider value={{ size: local.size }}>
<ArkPagination.Root
class={styles.root({ class: local.class, size: local.size })}
{...others}
/>
</PaginationVariantContext.Provider>
);
};
const RootProvider: Component<ArkPagination.RootProviderProps & PaginationVariants> = (props) => {
const [local, others] = splitProps(props, ["class", "size"]);
return (
<PaginationVariantContext.Provider value={{ size: local.size }}>
<ArkPagination.RootProvider
class={styles.root({ class: local.class, size: local.size })}
{...others}
/>
</PaginationVariantContext.Provider>
);
};
const Ellipsis: Component<ArkPagination.EllipsisProps> = (props) => {
return <ArkPagination.Ellipsis {...props} />;
};
const FirstTrigger: Component<ArkPagination.FirstTriggerProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkPagination.FirstTrigger class={styles.trigger({ class: local.class })} {...others} />;
};
const Item: Component<ArkPagination.ItemProps & PaginationVariants> = (props) => {
const ctx = usePaginationVariant();
const [local, others] = splitProps(props, ["class", "size"]);
return (
<ArkPagination.Item
class={styles.item({
class: local.class,
size: local.size ?? ctx?.size,
})}
{...others}
/>
);
};
const LastTrigger: Component<ArkPagination.LastTriggerProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkPagination.LastTrigger class={styles.trigger({ class: local.class })} {...others} />;
};
const NextTrigger: Component<ArkPagination.NextTriggerProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkPagination.NextTrigger class={styles.trigger({ class: local.class })} {...others} />;
};
const PrevTrigger: Component<ArkPagination.PrevTriggerProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkPagination.PrevTrigger class={styles.trigger({ class: local.class })} {...others} />;
};
export const Pagination = {
Root,
RootProvider,
Ellipsis,
FirstTrigger,
Item,
LastTrigger,
NextTrigger,
PrevTrigger,
};
export { PaginationVariantContext, usePaginationVariant };src/components/pagination/index.tsx:
import { splitProps, For, type Component } from "solid-js";
import { Pagination as PaginationBase } from "./pagination.base";
import { Pagination as ArkPagination } from "@ark-ui/solid/pagination";
import type { PaginationVariants } from "../recipes/pagination";
const Pagination: Component<ArkPagination.RootProps & PaginationVariants> = (props) => {
const [local, others] = splitProps(props, ["class", "size", "children"]);
return (
<PaginationBase.Root class={local.class} size={local.size} {...others}>
{local.children}
</PaginationBase.Root>
);
};
const PaginationRoot = Pagination;
const PaginationItem: Component<ArkPagination.ItemProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <PaginationBase.Item class={local.class} {...others} />;
};
const PaginationPrevTrigger = PaginationBase.PrevTrigger;
const PaginationNextTrigger = PaginationBase.NextTrigger;
const PaginationFirstTrigger = PaginationBase.FirstTrigger;
const PaginationLastTrigger = PaginationBase.LastTrigger;
const PaginationEllipsis = PaginationBase.Ellipsis;
const PaginationPageList: Component = () => {
return (
<ArkPagination.Context>
{(pagination) => (
<For each={pagination().pages}>
{(page, index) =>
page.type === "page" ? (
<PaginationBase.Item type="page" value={page.value}>
{page.value}
</PaginationBase.Item>
) : (
<PaginationBase.Ellipsis index={index()}>...</PaginationBase.Ellipsis>
)
}
</For>
)}
</ArkPagination.Context>
);
};
export {
Pagination,
PaginationRoot,
PaginationItem,
PaginationPrevTrigger,
PaginationNextTrigger,
PaginationFirstTrigger,
PaginationLastTrigger,
PaginationEllipsis,
PaginationPageList,
PaginationBase,
};
export { paginationVariants, type PaginationVariants } from "../recipes/pagination";Note: Make sure your project has the Tailwind CSS theme variables set up (
--background,--foreground,--ring,--border, etc.) or override the utility classes to match your design system.
Usage
Controlled Page
Use page and onPageChange to control the current page externally.
Controlled page: 5
import { createSignal } from "solid-js";
import {
Pagination,
PaginationPageList,
PaginationFirstTrigger,
PaginationPrevTrigger,
PaginationNextTrigger,
PaginationLastTrigger,
} from "~/components/pagination";
export default function PaginationControlledDemo() {
const [page, setPage] = createSignal(5);
return (
<div>
<div>
<p class="text-sm text-muted-foreground mb-3">
Controlled page: <span class="font-medium text-foreground">{page()}</span>
</p>
<Pagination
count={100}
pageSize={10}
page={page()}
onPageChange={(e) => setPage(e.page)}
class="gap-1"
>
<PaginationFirstTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="11 17 6 12 11 7" />
<polyline points="18 17 13 12 18 7" />
</svg>
</PaginationFirstTrigger>
<PaginationPrevTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6" />
</svg>
</PaginationPrevTrigger>
<PaginationPageList />
<PaginationNextTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6" />
</svg>
</PaginationNextTrigger>
<PaginationLastTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="13 17 18 12 13 7" />
<polyline points="6 17 11 12 6 7" />
</svg>
</PaginationLastTrigger>
</Pagination>
</div>
</div>
);
}Size Variants
Use the size prop to control button dimensions — sm (h-7 min-w-7), md (h-8 min-w-8), lg (h-9 min-w-9).
Size sm
Size md
Size lg
import {
Pagination,
PaginationPageList,
PaginationFirstTrigger,
PaginationPrevTrigger,
PaginationNextTrigger,
PaginationLastTrigger,
} from "~/components/pagination";
function PaginationSized(props: { size: "sm" | "md" | "lg"; label: string }) {
return (
<div>
<p class="text-sm text-muted-foreground mb-3">
Size <span class="font-medium text-foreground">{props.label}</span>
</p>
<Pagination count={100} pageSize={10} size={props.size} class="gap-1">
<PaginationFirstTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="11 17 6 12 11 7" />
<polyline points="18 17 13 12 18 7" />
</svg>
</PaginationFirstTrigger>
<PaginationPrevTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6" />
</svg>
</PaginationPrevTrigger>
<PaginationPageList />
<PaginationNextTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6" />
</svg>
</PaginationNextTrigger>
<PaginationLastTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="13 17 18 12 13 7" />
<polyline points="6 17 11 12 6 7" />
</svg>
</PaginationLastTrigger>
</Pagination>
</div>
);
}
export default function PaginationSizeDemo() {
return (
<div class="flex flex-col gap-6">
<PaginationSized size="sm" label="sm" />
<PaginationSized size="md" label="md" />
<PaginationSized size="lg" label="lg" />
</div>
);
}Advanced Usage
RootProvider Pattern
For full control over the pagination machine state, use usePagination with PaginationBase.RootProvider.
RootProvider pattern
import { For } from "solid-js";
import { usePagination } from "@ark-ui/solid/pagination";
import { PaginationBase } from "~/components/pagination";
export default function PaginationRootProviderDemo() {
const pagination = usePagination({ count: 100, pageSize: 10 });
return (
<div>
<div>
<p class="text-sm text-muted-foreground mb-3">RootProvider pattern</p>
<PaginationBase.RootProvider value={pagination} class="gap-1">
<PaginationBase.FirstTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="11 17 6 12 11 7" />
<polyline points="18 17 13 12 18 7" />
</svg>
</PaginationBase.FirstTrigger>
<PaginationBase.PrevTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6" />
</svg>
</PaginationBase.PrevTrigger>
<For each={pagination().pages}>
{(page, _index) =>
page.type === "page" ? (
<PaginationBase.Item type="page" value={page.value}>
{page.value}
</PaginationBase.Item>
) : (
<PaginationBase.Ellipsis index={_index()}>...</PaginationBase.Ellipsis>
)
}
</For>
<PaginationBase.NextTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6" />
</svg>
</PaginationBase.NextTrigger>
<PaginationBase.LastTrigger>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="13 17 18 12 13 7" />
<polyline points="6 17 11 12 6 7" />
</svg>
</PaginationBase.LastTrigger>
</PaginationBase.RootProvider>
</div>
</div>
);
}API Reference
See the Ark UI Pagination documentation.