@lattice-ui/react-context-menuFeature limitedimport ContextMenudepends on runtime, layer, motion, popperContext Menu is the primitive for actions that belong to a specific object rather than to a button: right-click a slot, a plot, a player name, and get a menu at the pointer. It differs from Menu in one decisive way — the menu is anchored to where you clicked, not to the element you clicked. The trigger is a region, not a button.
Reach for Context Menu when the action list belongs to a region of the screen, when the menu should appear under the pointer, and when a secondary (right) click is the natural way to ask for it. Reach for Menu instead when a visible button opens the list, or when gamepad and keyboard users must be able to move through the items.
Context Menu opens on MouseButton2 and tracks hover on its items (exposed through useContextMenuItemContext, for you to render). Unlike Menu, its items do not register with the focus manager: there is no automatic focus of the first item, no Up/Down movement, and no focus restore on close. On a gamepad-first or keyboard-first surface, use Menu.
The component running live in the browser — the same @rbxts/react tree Roblox renders, fully interactive. Right-click inside the card to open the menu at the pointer.
import { ContextMenu } from "@lattice-ui/react-context-menu";import { React } from "@lattice-ui/react-runtime";import { mergeGuiProps, Text, useTheme } from "@lattice-ui/react-style";import { panelRecipe } from "../../../playground/src/client/theme/recipes";
const actions = ["Rename", "Duplicate", "Move to…"];
function ContextMenuExample() { const { theme } = useTheme();
return ( <ContextMenu.Root> <ContextMenu.Trigger asChild> <textbutton {...(mergeGuiProps(panelRecipe({ tone: "elevated" }, theme), { AutoButtonColor: false, Size: UDim2.fromOffset(320, 180), Text: "", }) as Record<string, unknown>)} > <uicorner CornerRadius={new UDim(0, theme.radius.lg)} /> <uistroke Color={theme.colors.border} Thickness={1} />
<Text AnchorPoint={new Vector2(0.5, 0.5)} BackgroundTransparency={1} Font={Enum.Font.GothamBold} Position={UDim2.fromScale(0.5, 0.42)} Size={UDim2.fromOffset(280, 20)} Text="Project card" TextColor3={theme.colors.textPrimary} TextSize={theme.typography.bodyMd.textSize} /> <Text AnchorPoint={new Vector2(0.5, 0.5)} BackgroundTransparency={1} Position={UDim2.fromScale(0.5, 0.58)} Size={UDim2.fromOffset(280, 16)} Text="Right-click anywhere inside this card" TextColor3={theme.colors.textSecondary} TextSize={theme.typography.labelSm.textSize} /> </textbutton> </ContextMenu.Trigger>
<ContextMenu.Portal> <ContextMenu.Content asChild> <frame {...(mergeGuiProps(panelRecipe({ tone: "elevated" }, theme), { AutomaticSize: Enum.AutomaticSize.Y, Size: UDim2.fromOffset(200, 0), }) as Record<string, unknown>)} > <uicorner CornerRadius={new UDim(0, theme.radius.md)} /> <uistroke Color={theme.colors.border} Thickness={1} /> <uipadding PaddingBottom={new UDim(0, theme.space[6])} PaddingLeft={new UDim(0, theme.space[6])} PaddingRight={new UDim(0, theme.space[6])} PaddingTop={new UDim(0, theme.space[6])} /> <uilistlayout FillDirection={Enum.FillDirection.Vertical} Padding={new UDim(0, theme.space[2])} />
<ContextMenu.Label asChild> <Text BackgroundTransparency={1} LayoutOrder={0} Size={UDim2.fromOffset(188, 24)} Text="Card" TextColor3={theme.colors.textSecondary} TextSize={theme.typography.labelSm.textSize} TextXAlignment={Enum.TextXAlignment.Left} > <uipadding PaddingLeft={new UDim(0, theme.space[8])} /> </Text> </ContextMenu.Label>
{actions.map((action, index) => ( // Items stay BackgroundTransparency=1: the primitive drives // BackgroundColor3 between fixed dark hover colors, which would // fight the light theme. See the Select scene for the same trade. <ContextMenu.Item asChild key={action}> <textbutton AutoButtonColor={false} BackgroundTransparency={1} BorderSizePixel={0} LayoutOrder={index + 1} Size={UDim2.fromOffset(188, 32)} Text={action} TextColor3={theme.colors.textPrimary} TextSize={theme.typography.labelSm.textSize} TextXAlignment={Enum.TextXAlignment.Left} > <uicorner CornerRadius={new UDim(0, theme.radius.sm)} /> <uipadding PaddingLeft={new UDim(0, theme.space[8])} /> </textbutton> </ContextMenu.Item> ))}
<ContextMenu.Separator asChild> <frame BackgroundColor3={theme.colors.border} BorderSizePixel={0} LayoutOrder={actions.size() + 1} Size={UDim2.fromOffset(188, 1)} /> </ContextMenu.Separator>
<ContextMenu.Item asChild> <textbutton AutoButtonColor={false} BackgroundTransparency={1} BorderSizePixel={0} LayoutOrder={actions.size() + 2} Size={UDim2.fromOffset(188, 32)} Text="Delete" TextColor3={theme.colors.danger} TextSize={theme.typography.labelSm.textSize} TextXAlignment={Enum.TextXAlignment.Left} > <uicorner CornerRadius={new UDim(0, theme.radius.sm)} /> <uipadding PaddingLeft={new UDim(0, theme.space[8])} /> </textbutton> </ContextMenu.Item> </frame> </ContextMenu.Content> </ContextMenu.Portal> </ContextMenu.Root> );}Import
import { ContextMenu } from "@lattice-ui/react-context-menu";Anatomy
Root, Trigger, Portal, and Content form the working menu; Item makes it useful, and Group, Label, and Separator structure longer lists. The part names match Menu’s, so moving between the two is mostly a matter of swapping the namespace.
Context Menu anatomy
<ContextMenu.Root> <ContextMenu.Trigger /> <ContextMenu.Portal> <ContextMenu.Content> <ContextMenu.Label /> <ContextMenu.Group> <ContextMenu.Item /> </ContextMenu.Group> <ContextMenu.Separator /> <ContextMenu.Item /> </ContextMenu.Content> </ContextMenu.Portal></ContextMenu.Root>| Part | Required | Responsibility |
|---|---|---|
ContextMenu.Root | yes | Owns open state and the pointer position the menu opens at. |
ContextMenu.Trigger | yes | The region that listens for a secondary click and reports where it happened. |
ContextMenu.Portal | yes | Renders the surface into a ScreenGui outside the local tree. |
ContextMenu.Content | yes | The pointer-anchored, dismissable surface. |
ContextMenu.Item | yes | A clickable action that emits onSelect and closes the menu. |
ContextMenu.Group | no | A container that groups related items. Supply the layout yourself. |
ContextMenu.Label | no | A non-interactive heading for a group or section. |
ContextMenu.Separator | no | A thin divider between items or groups. |
Examples
Basic context menu
An uncontrolled root, a trigger region, and a few items. Right-clicking anywhere inside the trigger opens the menu at the pointer; selecting an item runs its onSelect and closes it.
Every part renders unstyled, so this example supplies all of it: a size and color for the trigger, a layout and surface for the content, and a size plus label for each item. Nothing here is optional decoration — without it the menu opens and works, but draws nothing.
import { ContextMenu } from "@lattice-ui/react-context-menu";
const ITEMS = [ { label: "Rename", action: () => print("rename") }, { label: "Duplicate", action: () => print("duplicate") }, { label: "Delete", action: () => print("delete") },];
export function BasicContextMenu() { return ( <ContextMenu.Root> <ContextMenu.Trigger BackgroundColor3={Color3.fromRGB(32, 36, 46)} Size={UDim2.fromOffset(280, 160)} Text="Right-click here" TextColor3={Color3.fromRGB(150, 158, 176)} />
<ContextMenu.Portal> <ContextMenu.Content BackgroundColor3={Color3.fromRGB(28, 31, 40)}> <uicorner CornerRadius={new UDim(0, 8)} /> <uipadding PaddingBottom={new UDim(0, 4)} PaddingTop={new UDim(0, 4)} /> <uilistlayout FillDirection={Enum.FillDirection.Vertical} />
{ITEMS.map((item) => ( <ContextMenu.Item key={item.label} onSelect={item.action} Size={UDim2.fromOffset(220, 34)} Text={item.label} TextColor3={Color3.fromRGB(236, 241, 249)} TextXAlignment={Enum.TextXAlignment.Left} > <uipadding PaddingLeft={new UDim(0, 10)} /> </ContextMenu.Item> ))} </ContextMenu.Content> </ContextMenu.Portal> </ContextMenu.Root> );}ContextMenu.Item still follows the pointer, but since 0.7.0 it no longer paints a highlight. Read the state with useContextMenuItemContext() and render it yourself — see Highlighting items below.
Omit open/onOpenChange and the root owns its state, starting from defaultOpen (closed by default). The pointer position is always owned by the root — even when you control open yourself, the anchor comes from the last secondary click on the trigger.
A real trigger region
asChild merges the secondary-click listener onto your own element, which is the normal way to use this primitive: the trigger is the thing the actions belong to. The child keeps all of its own props and handlers — the slot only adds InputBegan and Active — so a left-click Activated on the same element still does whatever it did before.
import { ContextMenu } from "@lattice-ui/react-context-menu";
export function PlotContextMenu(props: { plotName: string; onSelect: () => void }) { return ( <ContextMenu.Root> <ContextMenu.Trigger asChild> <textbutton Size={UDim2.fromOffset(320, 200)} Text={props.plotName} Event={{ Activated: props.onSelect }} /> </ContextMenu.Trigger>
<ContextMenu.Portal> <ContextMenu.Content> <frame AutomaticSize={Enum.AutomaticSize.Y} BackgroundColor3={Color3.fromRGB(28, 32, 42)} Size={UDim2.fromOffset(200, 0)} > <uilistlayout Padding={new UDim(0, 2)} SortOrder={Enum.SortOrder.LayoutOrder} /> <uicorner CornerRadius={new UDim(0, 8)} />
<ContextMenu.Item onSelect={() => print("build")}> <textbutton Text="Build here" /> </ContextMenu.Item> <ContextMenu.Item onSelect={() => print("clear")}> <textbutton Text="Clear plot" /> </ContextMenu.Item> </frame> </ContextMenu.Content> </ContextMenu.Portal> </ContextMenu.Root> );}Groups, labels, and separators
Group wraps related items in a 220-wide vertical-layout frame, Label puts a muted, non-interactive heading above them, and Separator draws a 1px divider. None of them are interactive — they exist to give a long list structure.
import { ContextMenu } from "@lattice-ui/react-context-menu";
export function InventorySlotContextMenu(props: { icon: string }) { return ( <ContextMenu.Root> <ContextMenu.Trigger asChild> <imagebutton Image={props.icon} Size={UDim2.fromOffset(64, 64)} /> </ContextMenu.Trigger>
<ContextMenu.Portal> <ContextMenu.Content> <frame AutomaticSize={Enum.AutomaticSize.Y} BackgroundColor3={Color3.fromRGB(28, 32, 42)} Size={UDim2.fromOffset(220, 0)} > <uilistlayout Padding={new UDim(0, 2)} SortOrder={Enum.SortOrder.LayoutOrder} /> <uicorner CornerRadius={new UDim(0, 8)} />
<ContextMenu.Label asChild> <textlabel BackgroundTransparency={1} Size={UDim2.fromOffset(220, 24)} Text="Equipment" TextColor3={Color3.fromRGB(162, 173, 191)} /> </ContextMenu.Label> <ContextMenu.Group> <ContextMenu.Item onSelect={() => print("equip")}> <textbutton Text="Equip" /> </ContextMenu.Item> <ContextMenu.Item onSelect={() => print("inspect")}> <textbutton Text="Inspect" /> </ContextMenu.Item> </ContextMenu.Group>
<ContextMenu.Separator />
<ContextMenu.Item onSelect={() => print("drop")}> <textbutton Text="Drop" TextColor3={Color3.fromRGB(244, 120, 120)} /> </ContextMenu.Item> </frame> </ContextMenu.Content> </ContextMenu.Portal> </ContextMenu.Root> );}Placement tuning
ContextMenu.Content takes the same popper options as the other anchored primitives, but the anchor is a zero-height virtual frame at the pointer, as wide as the measured content. That is what makes the default placement="bottom" drop the menu’s top-left corner at the cursor, the way a desktop context menu behaves. Change placement when you want it to grow the other way — near the bottom of the screen the popper flips it for you regardless.
import { ContextMenu } from "@lattice-ui/react-context-menu";
export function MinimapContextMenu() { return ( <ContextMenu.Root> <ContextMenu.Trigger asChild> <imagebutton Image="rbxassetid://0" Size={UDim2.fromOffset(180, 180)} /> </ContextMenu.Trigger>
<ContextMenu.Portal> <ContextMenu.Content placement="bottom" sideOffset={2} collisionPadding={16}> <frame AutomaticSize={Enum.AutomaticSize.Y} BackgroundColor3={Color3.fromRGB(28, 32, 42)} Size={UDim2.fromOffset(190, 0)} > <uilistlayout Padding={new UDim(0, 2)} SortOrder={Enum.SortOrder.LayoutOrder} />
<ContextMenu.Item onSelect={() => print("ping")}> <textbutton Text="Ping location" /> </ContextMenu.Item> <ContextMenu.Item onSelect={() => print("waypoint")}> <textbutton Text="Set waypoint" /> </ContextMenu.Item> </frame> </ContextMenu.Content> </ContextMenu.Portal> </ContextMenu.Root> );}Highlighting items
The item tracks hover for you but does not paint it. useContextMenuItemContext() returns { highlighted, disabled } — highlighted is already false while disabled, so one branch covers both.
Read it from a component rendered inside the item, since that is where the context lives:
import { ContextMenu, useContextMenuItemContext } from "@lattice-ui/react-context-menu";
function RowSurface(props: { label: string }) { const { highlighted, disabled } = useContextMenuItemContext();
return ( <frame BackgroundColor3={Color3.fromRGB(64, 84, 138)} BackgroundTransparency={highlighted ? 0 : 1} BorderSizePixel={0} Size={UDim2.fromScale(1, 1)} > <uicorner CornerRadius={new UDim(0, 4)} /> <textlabel BackgroundTransparency={1} Size={UDim2.fromScale(1, 1)} Text={props.label} TextColor3={disabled ? Color3.fromRGB(110, 116, 132) : Color3.fromRGB(236, 241, 249)} TextXAlignment={Enum.TextXAlignment.Left} /> </frame> );}
export function ContextMenuRow(props: { label: string; disabled?: boolean }) { return ( <ContextMenu.Item disabled={props.disabled} Size={UDim2.fromOffset(220, 34)}> <RowSurface label={props.label} /> </ContextMenu.Item> );}Because the highlight is now yours, you also choose whether it animates. Wrap the transparency in a response motion if you want the old eased feel.
Disabled items and staying open
disabled on an item blocks activation and clears its highlight state. onSelect receives a ContextMenuSelectEvent; calling event.preventDefault() marks it default-prevented and the item skips the automatic close — the one thing the default behavior does — so the menu stays open. That makes toggle-style items possible.
import { useState } from "@rbxts/react";import { ContextMenu } from "@lattice-ui/react-context-menu";
export function MarkerContextMenu(props: { canDelete: boolean }) { const [pinned, setPinned] = useState(false);
return ( <ContextMenu.Root> <ContextMenu.Trigger asChild> <textbutton Size={UDim2.fromOffset(240, 120)} Text="Marker" /> </ContextMenu.Trigger>
<ContextMenu.Portal> <ContextMenu.Content> <frame AutomaticSize={Enum.AutomaticSize.Y} BackgroundColor3={Color3.fromRGB(28, 32, 42)} Size={UDim2.fromOffset(200, 0)} > <uilistlayout Padding={new UDim(0, 2)} SortOrder={Enum.SortOrder.LayoutOrder} />
{/* Stays open so you can see the state flip. */} <ContextMenu.Item onSelect={(event) => { event.preventDefault(); setPinned(!pinned); }} > <textbutton Text={pinned ? "Unpin" : "Pin"} /> </ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Item disabled={!props.canDelete} onSelect={() => print("delete")}> <textbutton Text="Delete" /> </ContextMenu.Item> </frame> </ContextMenu.Content> </ContextMenu.Portal> </ContextMenu.Root> );}Controlled open state
Pass open and onOpenChange when something outside the menu needs to close it — a round ending, a selection being cleared, a different panel taking over. The trigger still owns where the menu appears, so controlling open does not mean you have to supply a position.
import { useEffect, useState } from "@rbxts/react";import { ContextMenu } from "@lattice-ui/react-context-menu";
export function ControlledContextMenu(props: { editable: boolean }) { const [open, setOpen] = useState(false);
// Leaving edit mode should take the menu with it. useEffect(() => { if (!props.editable) { setOpen(false); } }, [props.editable]);
return ( <ContextMenu.Root open={open} onOpenChange={setOpen}> <ContextMenu.Trigger disabled={!props.editable} asChild> <textbutton Size={UDim2.fromOffset(320, 200)} Text="Canvas" /> </ContextMenu.Trigger>
<ContextMenu.Portal> <ContextMenu.Content> <ContextMenu.Item onSelect={() => print("cut")} /> <ContextMenu.Item onSelect={() => print("paste")} /> </ContextMenu.Content> </ContextMenu.Portal> </ContextMenu.Root> );}How it behaves
Open state and the anchor
ContextMenu.Root is controllable on open/onOpenChange, with defaultOpen for uncontrolled usage (defaulting to closed). ContextMenu.Trigger watches InputBegan and reacts only to Enum.UserInputType.MouseButton2: it converts the raw pointer position into the inset-adjusted space that GuiObject.AbsolutePosition uses, stores it on the root, and opens the menu. That stored position survives until the next secondary click, so a controlled root can reopen the menu at the same spot.
A disabled trigger ignores the secondary click entirely and never updates the stored position.
Positioning
ContextMenu.Content mounts an invisible virtual anchor at the stored pointer position and hands it to the shared popper machinery. The anchor has zero height and the measured content’s width, which is what makes the resolved placement land the menu’s top-left corner at the cursor instead of centering it under the pointer.
From there it behaves like every other anchored surface: the requested placement (default "bottom") is a preference, and on collision the popper tries the opposite side, then the orthogonal sides, then clamps the best candidate inside the viewport with collisionPadding (default 8) kept from every edge. sideOffset and alignOffset shift the result. Until the first measurement completes the content is parked off-screen, so it never flashes at the wrong position.
Items and activation
ContextMenu.Item activates on Activated and builds a ContextMenuSelectEvent ({ defaultPrevented, preventDefault() }) for onSelect; if the event is not default-prevented, the item closes the menu. A disabled item ignores activation.
The item renders an unstyled textbutton. It tracks hover through MouseEnter/MouseLeave and exposes the result as useContextMenuItemContext().highlighted, but draws nothing itself — rendering the highlight is yours, under asChild or not.
Dismissal
ContextMenu.Content participates in dismissable-layer behavior: only the top-most enabled layer receives outside interactions, so nested overlays dismiss one at a time. Context Menu is modal by default, so a full-screen blocker stops interaction behind the surface and an outside press dismisses the menu. Before dismissal, onPointerDownOutside fires for outside pointer presses and onInteractOutside fires for the interaction in general; both receive a LayerInteractEvent ({ originalEvent, defaultPrevented, preventDefault() }), and calling preventDefault() in either handler vetoes the dismissal while still letting you observe the interaction.
Motion and presence
ContextMenu.Content runs no motion of its own. Pass a transition to animate it; createPopperEntranceRecipe(placement) from @lattice-ui/react-motion matches the frame the content renders, and taking the resolved placement makes the motion originate from the side the menu actually landed on — so a menu the popper flipped above the pointer animates from above. forceMount keeps the content mounted through its exit. The content wrapper is an automatically-sized frame, so your surface defines the measured size.
ContextMenu.Item does not register a focus node. There is no automatic focus on open, no Up/Down movement between items, and no focus restore on close — modal here means “blocks pointer interaction behind the surface”, not “traps selection”. If your surface has to be operable without a mouse, build it with Menu instead.
Before 0.7.0 ContextMenu.Item animated your element’s BackgroundColor3 between fixed hover colors with no opt-out. It no longer touches color at all — it only reports highlighted through useContextMenuItemContext(). Set the background yourself and it will stay put.
API reference
ContextMenu.Root
| Prop | Type | Description |
|---|---|---|
| open | boolean | Controlled open state. Pair with onOpenChange. |
| defaultOpen | boolean | Initial open state for uncontrolled usage. Defaults to false. |
| onOpenChange | (open: boolean) => void | Called whenever the open state changes. |
| modal | boolean | When true, blocks pointer interaction behind the surface with a full-screen blocker. Defaults to true. |
| children | React.ReactNode | The context menu parts. |
ContextMenu.Trigger
| Prop | Type | Description |
|---|---|---|
| asChild | boolean | Merge the secondary-click listener onto the single child element instead of the textbutton the part renders. |
| disabled | boolean | Ignores the secondary click, so the menu never opens from this region. |
| children | React.ReactElement | The element to render. Required when asChild is set. |
ContextMenu.Portal
| Prop | Type | Description |
|---|---|---|
| container | BasePlayerGui | Target PlayerGui to render the surface into. Defaults to the surrounding portal context's container. |
| displayOrderBase | number | Base DisplayOrder for the generated ScreenGui, used to order it against other layers. Defaults to the surrounding portal context's value. |
| children | React.ReactNode | The content part. |
ContextMenu.Content
| Prop | Type | Description |
|---|---|---|
| placement | "top" | "bottom" | "left" | "right" | Requested side to position the content on, relative to the pointer anchor. Falls back to the opposite side, then the orthogonal sides, on collision. Defaults to "bottom". |
| sideOffset | number | Gap in pixels between the pointer anchor and the content. Defaults to 0. |
| alignOffset | number | Shift in pixels along the anchor's cross axis. Defaults to 0. |
| collisionPadding | number | Minimum distance in pixels to keep from every screen edge when resolving and clamping placement. Defaults to 8. |
| asChild | boolean | Render the single child element inside the positioned frame instead of the part's own children. |
| forceMount | boolean | Keeps the content mounted while exit motion runs, instead of unmounting on close. |
| transition | PresenceMotionConfig | Reveal/exit motion. None by default; pass createPopperEntranceRecipe(placement) for a placement-aware entrance. |
| onPointerDownOutside | (event: LayerInteractEvent) => void | Called when a pointer press occurs outside the content, before dismissal. Call event.preventDefault() to veto the dismissal. |
| onInteractOutside | (event: LayerInteractEvent) => void | Called for any outside interaction, before dismissal. Call event.preventDefault() to veto the dismissal. |
| children | React.ReactNode | The menu contents. |
ContextMenu.Item
Renders a TextButton. Unknown props forward onto it and are type-checked against it, so a prop TextButton does not accept is a compile error. The primitive owns Active and Selectable derived from disabled, so values you pass for those are ignored.
| Prop | Type | Description |
|---|---|---|
| asChild | boolean | Merge item behavior onto the single child element instead of the textbutton the part renders. The child must be an activatable button. |
| disabled | boolean | Prevents activation and forces highlighted to false. |
| onSelect | (event: ContextMenuSelectEvent) => void | Called on activation. Call event.preventDefault() to keep the menu open. |
| children | React.ReactNode | The item contents. Must be a single element when asChild is set. |
| …TextButton props | Partial<WritableInstanceProperties<TextButton>> | Forwarded onto the rendered textbutton and type-checked against it. Active and Selectable are owned by the primitive, derived from disabled. |
ContextMenu.Group
Renders a Frame. Unknown props forward onto it and are type-checked against it, so a prop Frame does not accept is a compile error.
As of 0.7.0 ContextMenu.Group renders a plain frame: no UIListLayout, no AutomaticSize. It was one of the last two primitives holding an opinion about how children lay out. Add a uilistlayout child, as Select.Group and Combobox.Group have always required.
| Prop | Type | Description |
|---|---|---|
| asChild | boolean | Merge the group onto the single child element instead of the frame the part renders. |
| children | React.ReactNode | The grouped items, plus the layout that arranges them. Must be a single element when asChild is set. |
| …Frame props | Partial<WritableInstanceProperties<Frame>> | Forwarded onto the rendered frame and type-checked against it. |
ContextMenu.Label
| Prop | Type | Description |
|---|---|---|
| asChild | boolean | Merge the label onto the single child element instead of the textlabel the part renders. |
| children | React.ReactNode | The label contents. Must be a single element when asChild is set. |
| …TextLabel props | Partial<WritableInstanceProperties<TextLabel>> | Forwarded onto the rendered textlabel and type-checked against it. Pass Text and TextColor3 here — the part renders no copy of its own. |
ContextMenu.Separator
| Prop | Type | Description |
|---|---|---|
| asChild | boolean | Merge the separator onto the single child element instead of the frame the part renders. |
| children | React.ReactNode | Rendered inside the separator. Must be a single element when asChild is set. |
| …Frame props | Partial<WritableInstanceProperties<Frame>> | Forwarded onto the rendered frame and type-checked against it. Give it a Size and BackgroundColor3 — it draws nothing on its own. |