Popover
Displays rich content in a portal, triggered by a button.
import * as stylex from '@stylexjs/stylex';
import { space, fontSize, fontWeight } from '@/lib/constants.stylex';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
export default function PopoverDemo() {
return (
<Popover>
<PopoverTrigger render={<Button variant="outline" />}>
Open popover
</PopoverTrigger>
<PopoverContent>
<div {...stylex.props(styles.form)}>
<strong {...stylex.props(styles.heading)}>Dimensions</strong>
<Input placeholder="Width" defaultValue="100%" />
<Input placeholder="Height" defaultValue="25px" />
</div>
</PopoverContent>
</Popover>
);
}
const styles = stylex.create({
form: {
display: 'flex',
flexDirection: 'column',
gap: space.s2,
},
heading: {
fontSize: fontSize.sm,
fontWeight: fontWeight.semibold,
},
});
Install
npx shadcn@latest add @madeui/popover
Usage
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from '@/components/ui/popover';
Composition
<Popover>
<PopoverTrigger />
<PopoverContent>
<PopoverHeader>
<PopoverTitle />
<PopoverDescription />
</PopoverHeader>
</PopoverContent>
</Popover>
Placement
import { Button } from '@/components/ui/button';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
export default function PopoverPlacement() {
return (
<Popover>
<PopoverTrigger render={<Button variant="outline" />}>
Open above
</PopoverTrigger>
<PopoverContent side="top" align="start">
Anchored to the top-start of the trigger.
</PopoverContent>
</Popover>
);
}
Header, title, description
PopoverHeader is a plain layout wrapper; PopoverTitle and PopoverDescription are Base UI’s Popover.Title / Popover.Description, wiring up aria-labelledby / aria-describedby on the popup automatically.
import { Button } from '@/components/ui/button';
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from '@/components/ui/popover';
export default function PopoverParts() {
return (
<Popover>
<PopoverTrigger render={<Button variant="outline" />}>
Open popover
</PopoverTrigger>
<PopoverContent>
<PopoverHeader>
<PopoverTitle>Dimensions</PopoverTitle>
<PopoverDescription>Set the dimensions for the layer.</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>
);
}
With form
import * as stylex from '@stylexjs/stylex';
import { Button } from '@/components/ui/button';
import { Field, FieldLabel } from '@/components/ui/field';
import { Input } from '@/components/ui/input';
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from '@/components/ui/popover';
import { space } from '@/lib/constants.stylex';
export default function PopoverForm() {
return (
<Popover>
<PopoverTrigger render={<Button variant="outline" />}>
Edit profile
</PopoverTrigger>
<PopoverContent>
<PopoverHeader>
<PopoverTitle>Edit profile</PopoverTitle>
<PopoverDescription>Update your display name and handle.</PopoverDescription>
</PopoverHeader>
<form {...stylex.props(styles.form)}>
<Field>
<FieldLabel htmlFor="popover-form-name">Name</FieldLabel>
<Input id="popover-form-name" defaultValue="Evil Rabbit" />
</Field>
<Field>
<FieldLabel htmlFor="popover-form-handle">Handle</FieldLabel>
<Input id="popover-form-handle" defaultValue="@evilrabbit" />
</Field>
<Button type="submit" size="sm">
Save
</Button>
</form>
</PopoverContent>
</Popover>
);
}
const styles = stylex.create({
form: {
display: 'flex',
flexDirection: 'column',
gap: space.s3,
marginTop: space.s4,
},
});
API reference
Built on Base UI Popover. The tables below cover the props this library adds or changes — every other prop is forwarded to the underlying Base UI part; see the Base UI Popover API reference for the full list.
PopoverContent
| Prop | Type | Default | Description |
|---|---|---|---|
side |
'top' | 'right' | 'bottom' | 'left' |
'bottom' |
|
sideOffset |
number |
4 |
|
align |
'start' | 'center' | 'end' |
'center' |
|
alignOffset |
number |
0 |
|
style |
StyleXStyles |
— | StyleX styles merged last — always win over the component’s own styles. |
Popover (Root), PopoverTrigger, and PopoverClose are Base UI parts re-exported unstyled — Root accepts open / defaultOpen / onOpenChange; Trigger accepts render.
Styling
PopoverHeader, PopoverTitle, PopoverDescription accept style (StyleXStyles, merged last so caller overrides always win) plus all native props of the element they render.