Popover
Popover with thoughtful defaults, semantic styling, and editable source.
Make it yours.
Install the editable source and its dependencies into your React + Tailwind project.
Scroll horizontally for long lines
pnpm dlx shadcn@4.0.8 add http://localhost:3000/r/popover.jsonUse it
Scroll horizontally for long lines
"use client";
import {
Popover,
PopoverTrigger,
PopoverContent,
PopoverHeader,
PopoverTitle,
PopoverDescription,
} from "@/components/jez-ui/ui/popover";
import { Button } from "@/components/jez-ui/ui/button";
export default function Example() {
return (
<>
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Project notes</Button>
</PopoverTrigger>
<PopoverContent>
<PopoverHeader>
<PopoverTitle>A small reminder</PopoverTitle>
<PopoverDescription>
Leave room for the unexpected.
</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>
</>
);
}
Compose your own
These styled parts are included in the same install. Use children to build your layout; add className only when you want an override.
Scroll horizontally for long lines
import { Popover, PopoverTrigger, PopoverAnchor, PopoverClose, PopoverContent, PopoverHeader, PopoverTitle, PopoverDescription } from "@/components/jez-ui/ui/popover";Props & types
Scroll horizontally for long lines
React.ComponentProps<typeof P.Root> & {
trigger?: React.ReactNode;
className?: string;
}Source
View 2 source files
registry/ui/popover.tsx
Scroll horizontally for long lines
"use client";
import * as React from "react";
import { Popover as P } from "radix-ui";
import { cn } from "./utils";
export function Popover({
trigger,
children,
className,
...props
}: React.ComponentProps<typeof P.Root> & {
trigger?: React.ReactNode;
className?: string;
}) {
return (
<P.Root {...props}>
{trigger !== undefined ? (
<>
<PopoverTrigger asChild>{trigger}</PopoverTrigger>
<PopoverContent className={className}>{children}</PopoverContent>
</>
) : (
children
)}
</P.Root>
);
}
export const PopoverTrigger = P.Trigger;
export const PopoverAnchor = P.Anchor;
export const PopoverClose = P.Close;
export function PopoverContent({
className,
...props
}: React.ComponentProps<typeof P.Content>) {
return (
<P.Portal>
<P.Content
sideOffset={8}
align="start"
collisionPadding={16}
className={cn(
"jez-popover z-50 w-72 max-w-[calc(100vw-32px)] rounded-xl border border-border bg-background p-5 text-sm leading-relaxed text-foreground shadow-lg",
className,
)}
{...props}
/>
</P.Portal>
);
}
export function PopoverHeader({
className,
...props
}: React.ComponentProps<"div">) {
return <div className={cn("mb-4 grid gap-1.5", className)} {...props} />;
}
export function PopoverTitle({
className,
...props
}: React.ComponentProps<"h3">) {
return (
<h3 className={cn("font-medium leading-tight", className)} {...props} />
);
}
export function PopoverDescription({
className,
...props
}: React.ComponentProps<"p">) {
return (
<p className={cn("text-sm text-muted-foreground", className)} {...props} />
);
}
registry/ui/utils.ts
Scroll horizontally for long lines
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...values: ClassValue[]) {
return twMerge(clsx(values));
}
Dependencies
radix-ui@1.6.7 · clsx@2.1.1 · tailwind-merge@3.5.0
Accessibility & behaviour
Provide meaningful labels and preserve keyboard focus styles. Check contrast when customising theme colours.
Read the accessibility and performance guide →