Components
Pager
A controlled single-DOM pager with touch gesture dragging, loop and View-Transition page switching, built for manga-style readers and paged content
Installation
- $npx litefy@latest add pager
- $pnpm dlx litefy@latest add pager
- $yarn dlx litefy@latest add pager
- $bun --bun litefy@latest add pager
Usage
Fully controlled index / onChange; the root needs explicit width and height — it never adapts to content.
Alpha
1 / 4
"use client";
import * as React from "react";
import { Button, Pager } from "@/ui/components";
import { ChevronLeft, ChevronRight } from "lucide-react";
const pages = ["Alpha", "Bravo", "Charlie", "Delta"];
export default function PagerBasicDemo() {
const [index, setIndex] = React.useState(0);
return (
<div className="w-full space-y-3">
<Pager index={index} onChange={setIndex} className="h-48 w-full">
{pages.map((page) => (
<div
key={page}
className="flex h-full w-full items-center justify-center rounded-lg border bg-muted/40 text-2xl font-semibold"
>
{page}
</div>
))}
</Pager>
<div className="flex items-center justify-between">
<Button
variant="outline"
onClick={() => setIndex((i) => Math.max(0, i - 1))}
disabled={index === 0}
aria-label="Previous page"
>
<ChevronLeft className="size-4" />
</Button>
<span className="text-sm text-muted-foreground">
{index + 1} / {pages.length}
</span>
<Button
variant="outline"
onClick={() => setIndex((i) => Math.min(pages.length - 1, i + 1))}
disabled={index === pages.length - 1}
aria-label="Next page"
>
<ChevronRight className="size-4" />
</Button>
</div>
</div>
);
}
Gesture + Loop
Touch-drag paging with loop on to wrap around at both boundaries.
01
Touch drag horizontally — loop enabled, current page 1 / 5
"use client";import * as React from "react";import { Pager } from "@/ui/components";const pages = ["01", "02", "03", "04", "05"];export default function PagerGestureDemo() { const [index, setIndex] = React.useState(0); return ( <div className="w-full space-y-3"> <Pager index={index} onChange={setIndex} loop className="h-48 w-full"> {pages.map((page) => ( <div key={page} className="flex h-full w-full items-center justify-center rounded-lg border bg-muted/40 text-4xl font-bold tabular-nums" > {page} </div> ))} </Pager> <p className="text-center text-sm text-muted-foreground"> Touch drag horizontally — loop enabled, current page {index + 1} / {pages.length} </p> </div> );}API Reference
Pager
| Prop | Type | Default | Description |
|---|---|---|---|
index | number | - | Controlled current page index |
onChange | (nextIndex: number) => void | - | Fired after a gesture commit; the parent updates index |
children | React.ReactNode[] | - | One child per page; only the active page is mounted |
loop | boolean | false | Wrap around at both boundaries |
transition | "none" | "view-transition" | "view-transition" | Animation strategy for programmatic jumps (gesture drags are unaffected) |
gesture | boolean | true | Enable touch drag paging |
className | ClassNameValue | - | Custom class names — must include the width and height |
...props | React.ComponentProps<"div"> | - | Native div props, spread on the root container |