Live preview
"use client";
import * as React from "react";
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";
export default function PaginationDemo() {
const [page, setPage] = React.useState(2);
const selectPage = (event: React.MouseEvent<HTMLAnchorElement>, nextPage: number) => {
event.preventDefault();
setPage(nextPage);
window.history.replaceState(window.history.state, "", `?page=${nextPage}`);
};
return (
<Pagination aria-label={`Pagination, page ${page} of 3`}>
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href={`?page=${Math.max(1, page - 1)}`}
aria-disabled={page === 1}
tabIndex={page === 1 ? -1 : undefined}
onClick={(event) => selectPage(event, Math.max(1, page - 1))}
/>
</PaginationItem>
<PaginationItem>
<PaginationLink
href="?page=1"
isActive={page === 1}
onClick={(event) => selectPage(event, 1)}
>
1
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink
href="?page=2"
isActive={page === 2}
onClick={(event) => selectPage(event, 2)}
>
2
</PaginationLink>
</PaginationItem>
<PaginationItem className="hidden md:block">
<PaginationLink
href="?page=3"
isActive={page === 3}
onClick={(event) => selectPage(event, 3)}
>
3
</PaginationLink>
</PaginationItem>
{page < 3 ? (
<PaginationItem className="hidden md:block">
<PaginationEllipsis />
</PaginationItem>
) : null}
<PaginationItem>
<PaginationNext
href={`?page=${Math.min(3, page + 1)}`}
aria-disabled={page === 3}
tabIndex={page === 3 ? -1 : undefined}
onClick={(event) => selectPage(event, Math.min(3, page + 1))}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
);
}
#Installation
npx shadcn@latest add https://neobrutal-ui.andongmin.com/r/pagination.jsonimport * as React from "react";
import { cn } from "@/lib/utils";
import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
const paginationLinkClassName =
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-base border-2 border-border bg-main text-sm font-base text-main-foreground ring-offset-white transition-all focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0";
const paginationLinkSizeClassNames = {
default: "h-10 px-4 py-2",
sm: "h-9 px-3",
lg: "h-11 px-8",
icon: "size-10",
};
function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
return (
<nav
role="navigation"
aria-label="pagination"
data-slot="pagination"
className={cn("mx-auto flex w-full justify-center", className)}
{...props}
/>
);
}
function PaginationContent({ className, ...props }: React.ComponentProps<"ul">) {
return (
<ul
data-slot="pagination-content"
className={cn("flex flex-row items-center gap-1", className)}
{...props}
/>
);
}
function PaginationItem({ className, ...props }: React.ComponentProps<"li">) {
return <li data-slot="pagination-item" className={cn("", className)} {...props} />;
}
type PaginationLinkProps = {
isActive?: boolean;
size?: "default" | "sm" | "lg" | "icon";
} & React.ComponentProps<"a">;
function PaginationLink({ className, isActive, size = "icon", ...props }: PaginationLinkProps) {
return (
<a
data-slot="pagination-link"
aria-current={isActive ? "page" : undefined}
className={cn(
paginationLinkClassName,
paginationLinkSizeClassNames[size],
className,
isActive && "bg-black text-white",
)}
{...props}
/>
);
}
function PaginationPrevious({
className,
text = "Previous",
...props
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
return (
<PaginationLink
data-slot="pagination-previous"
aria-label="Go to previous page"
size="default"
className={cn("gap-1 pl-2.5", className)}
{...props}
>
<ChevronLeftIcon className="size-4" />
<span>{text}</span>
</PaginationLink>
);
}
function PaginationNext({
className,
text = "Next",
...props
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
return (
<PaginationLink
data-slot="pagination-next"
aria-label="Go to next page"
size="default"
className={cn("gap-1 pr-2.5", className)}
{...props}
>
<span>{text}</span>
<ChevronRightIcon className="size-4" />
</PaginationLink>
);
}
function PaginationEllipsis({ className, ...props }: React.ComponentProps<"span">) {
return (
<span
aria-hidden
data-slot="pagination-ellipsis"
className={cn(
"flex size-9 items-center justify-center [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
>
<MoreHorizontalIcon />
<span className="sr-only">More pages</span>
</span>
);
}
export {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
};
#Usage
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="#" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">1</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="#" isActive>
2
</PaginationLink>
</PaginationItem>
<PaginationItem className="hidden md:block">
<PaginationLink href="#">3</PaginationLink>
</PaginationItem>
<PaginationItem className="hidden md:block">
<PaginationEllipsis />
</PaginationItem>
<PaginationItem>
<PaginationNext href="#" />
</PaginationItem>
</PaginationContent>
</Pagination>