Popover
Popover component for displaying content on demand.
import { Popover, Button } from "ui-lab-components";
export function Example() {
return (
<Popover content="Popover content">
<Button>Click me</Button>
</Popover>
);
}Basic
Default popover with a short note and two clear actions.
"use client";
import { Popover, Button } from 'ui-lab-components';
export const metadata = {
title: 'Basic',
description: 'Default popover with a short note and two clear actions.'
};
export default function Example() {
return (
<Popover
content={
<div className="w-64 space-y-3">
<div className="space-y-1">
<div className="text-sm font-medium">Quick note</div>
<p>A popover works best when it adds one small piece of context, one simple choice, or one short action.</p>
</div>
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm">
Dismiss
</Button>
<Button size="sm">
Continue
</Button>
</div>
</div>
}
>
<Button>Show info</Button>
</Popover>
);
}
Toggleable Options
Popover containing a list of toggleable options using List.Item and List.Switch.
"use client";
import { useState } from 'react';
import { Popover, Button, List } from 'ui-lab-components';
export const metadata = {
title: 'Toggleable Options',
description: 'Popover containing a list of toggleable options using List.Item and List.Switch.'
};
const toggleableItems = [
{ id: "notifications", label: "Notifications", desc: "Push and email alerts" },
{ id: "autoSave", label: "Auto-save", desc: "Save changes automatically" },
{ id: "darkMode", label: "Dark mode", desc: "Use dark color scheme" },
];
export default function Example() {
const [enabled, setEnabled] = useState<Set<string>>(
() => new Set(["notifications", "darkMode"])
);
const toggle = (id: string) =>
setEnabled((prev) => {
const next = new Set(prev);
next.has(id) ? next.delete(id) : next.add(id);
return next;
});
return (
<Popover
position="bottom"
content={
<List items={toggleableItems} spacing="sm" style={{ width: 280 }}>
{toggleableItems.map((item) => (
<List.Item
key={item.id}
value={item.id}
interactive
onClick={() => toggle(item.id)}
>
<div className="min-w-0 flex-1">
<List.Title>{item.label}</List.Title>
<List.Desc>{item.desc}</List.Desc>
</div>
<List.Switch
isSelected={enabled.has(item.id)}
onChange={() => toggle(item.id)}
aria-label={item.label}
/>
</List.Item>
))}
</List>
}
>
<Button>Options</Button>
</Popover>
);
}
Table Row Actions
Per-row action menu in a data table, anchored to the overflow button.
| Name | Role | Status | |
|---|---|---|---|
| Alice | Admin | Active | |
| Bob | Member | Invited | |
| Carol | Viewer | Active |
"use client";
import { Popover, Button, List } from 'ui-lab-components';
import { FaEllipsis } from 'react-icons/fa6';
export const metadata = {
title: 'Table Row Actions',
description: 'Per-row action menu in a data table, anchored to the overflow button.'
};
const rows = [
{ id: "usr_1", name: "Alice", role: "Admin", status: "Active" },
{ id: "usr_2", name: "Bob", role: "Member", status: "Invited" },
{ id: "usr_3", name: "Carol", role: "Viewer", status: "Active" },
];
export default function Example() {
return (
<table className="w-full text-sm border-collapse">
<thead>
<tr className="border-b border-background-700">
<th className="text-left py-2 px-3 font-medium text-foreground-200">Name</th>
<th className="text-left py-2 px-3 font-medium text-foreground-200">Role</th>
<th className="text-left py-2 px-3 font-medium text-foreground-200">Status</th>
<th className="py-2 px-3" />
</tr>
</thead>
<tbody>
{rows.map((row) => (
<tr key={row.id} className="border-b border-background-700 last:border-0">
<td className="py-2 px-3">{row.name}</td>
<td className="py-2 px-3 text-foreground-200">{row.role}</td>
<td className="py-2 px-3 text-foreground-200">{row.status}</td>
<td className="py-2 px-3 text-right">
<Popover
position="left"
content={
<List gap="sm" styles={{ root: "w-full" }}>
<Button
variant="ghost"
size="sm"
styles={{ root: "justify-start" }}
>
Edit {row.name}
</Button>
<Button
variant="danger"
size="sm"
styles={{ root: "justify-start" }}
>
Remove {row.name}
</Button>
</List>
}
>
<Button icon={<FaEllipsis />} styles="p-2" size="icon" variant="ghost" aria-label={`Row actions for ${row.name}`} />
</Popover>
</td>
</tr>
))}
</tbody>
</table>
);
}
Input Form
Popover containing a small form with labeled input fields and save/cancel actions.
"use client";
import { useState } from 'react';
import { Popover, Button, Flex, Label, Input } from 'ui-lab-components';
export const metadata = {
title: 'Input Form',
description: 'Popover containing a small form with labeled input fields and save/cancel actions.'
};
export default function Example() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
return (
<Popover
position="bottom"
content={
<Flex direction="column" gap="sm" styles={{ root: "w-80" }}>
<Flex direction="column" gap="xs">
<Label htmlFor="contact-name">Name</Label>
<Input
id="contact-name"
type="text"
value={name}
onChange={(e) => setName((e.target as HTMLInputElement).value)}
placeholder="Full name"
/>
</Flex>
<Flex direction="column" gap="xs">
<Label htmlFor="contact-email">Email</Label>
<Input
id="contact-email"
type="email"
value={email}
onChange={(e) => setEmail((e.target as HTMLInputElement).value)}
placeholder="name@example.com"
/>
</Flex>
</Flex>
}
>
<Button>Edit contact</Button>
</Popover>
);
}
Arrow & Positions
Directional arrow enabled across all four placement options.
"use client";
import { Popover, Button, Flex } from 'ui-lab-components';
export const metadata = {
title: 'Arrow & Positions',
description: 'Directional arrow enabled across all four placement options.'
};
export default function Example() {
return (
<Flex align="center">
{(["top", "bottom", "left", "right"] as const).map((position) => (
<Popover
key={position}
position={position}
showArrow
content={<span className="text-sm capitalize">{position}</span>}
>
<Button variant="ghost">
<span className="capitalize">{position}</span>
</Button>
</Popover>
))}
</Flex>
);
}