Popover
Floating content anchored to an interactive trigger.
- Chromium
- Firefox
- WebKit
- WCAG 2.2 AA
Preview
import { Button } from "@moe-ui/registry/components/ui/button";
import { Input } from "@moe-ui/registry/components/ui/input";
import { Label } from "@moe-ui/registry/components/ui/label";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@moe-ui/registry/components/ui/popover";
import { Text } from "@moe-ui/registry/components/ui/text";
import { View } from "react-native";
export default function PopoverPreview() {
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">
<Text>Open popover</Text>
</Button>
</PopoverTrigger>
<PopoverContent className="w-80">
<View className="grid gap-4">
<View className="space-y-2">
<Text className="font-medium leading-none">Dimensions</Text>
<Text className="text-sm text-muted-foreground">
Set the dimensions for the layer.
</Text>
</View>
<View className="grid gap-2">
<View className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="width">Width</Label>
<Input
id="width"
defaultValue="100%"
className="col-span-2 h-8"
/>
</View>
<View className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="maxWidth">Max. width</Label>
<Input
id="maxWidth"
defaultValue="300px"
className="col-span-2 h-8"
/>
</View>
<View className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="height">Height</Label>
<Input
id="height"
defaultValue="25px"
className="col-span-2 h-8"
/>
</View>
<View className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="maxHeight">Max. height</Label>
<Input
id="maxHeight"
defaultValue="none"
className="col-span-2 h-8"
/>
</View>
</View>
</View>
</PopoverContent>
</Popover>
);
}
One command, your source
Installation
pnpm dlx @moe-ui/cli@beta add popoverUsage
The CLI installs editable source into your application. Import the component from your configured UI directory:
import { Popover } from "@/components/ui/popover";Variants and composition
Compound parts and variants remain regular source in your project, so you can change them without wrapping a package API.
Public API
The canonical source panel below lists every public export, dependency, and the complete implementation shipped by the registry.
Keyboard and accessibility
The trigger is keyboard reachable, Escape dismisses the surface, and focus returns to the trigger. Modal surfaces keep focus within the active layer.
Browser support
This beta is release-tested in Chromium, Firefox, and WebKit in light and dark themes. See the web compatibility matrix for the current gate.
Nothing hiddenCanonical source and public exports3 exports
PopoverPopoverContentPopoverTrigger"use client";
import { NativeOnlyAnimatedView } from "./native-only-animated-view";
import { TextClassContext } from "./text";
import { cn } from "../../lib/utils";
import * as PopoverPrimitive from "@rn-primitives/popover";
import * as React from "react";
import { Platform, StyleSheet } from "react-native";
import { FadeIn, FadeOut } from "react-native-reanimated";
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens";
const Popover = PopoverPrimitive.Root;
const PopoverTrigger = PopoverPrimitive.Trigger;
const FullWindowOverlay =
Platform.OS === "ios" ? RNFullWindowOverlay : React.Fragment;
function PopoverContent({
className,
align = "center",
sideOffset = 4,
portalHost,
...props
}: PopoverPrimitive.ContentProps &
React.RefAttributes<PopoverPrimitive.ContentRef> & {
portalHost?: string;
}) {
return (
<PopoverPrimitive.Portal hostName={portalHost}>
<FullWindowOverlay>
<PopoverPrimitive.Overlay
style={Platform.select({ native: StyleSheet.absoluteFill })}
>
<NativeOnlyAnimatedView
entering={FadeIn.duration(200)}
exiting={FadeOut}
>
<TextClassContext.Provider value="text-popover-foreground">
<PopoverPrimitive.Content
align={align}
sideOffset={sideOffset}
className={cn(
"bg-popover border-border outline-none z-50 w-72 rounded-md border p-4 shadow-md shadow-black/5",
Platform.select({
web: cn(
"animate-in fade-in-0 zoom-in-95 origin-[var(--radix-popover-content-transform-origin)] cursor-auto",
props.side === "bottom" && "slide-in-from-top-2",
props.side === "top" && "slide-in-from-bottom-2",
),
}),
className,
)}
{...props}
/>
</TextClassContext.Provider>
</NativeOnlyAnimatedView>
</PopoverPrimitive.Overlay>
</FullWindowOverlay>
</PopoverPrimitive.Portal>
);
}
export { Popover, PopoverContent, PopoverTrigger };