@lattice-ui/core Part of the stable direction toward v1.0.
What It Is For
Use @lattice-ui/core when you are building your own headless primitives or compound components on top of the same low-level conventions used by the rest of Lattice UI.
Most app teams consume it indirectly through other packages, but it is the place to reach for controllable state, slots, ref composition, and shared React bindings.
TSX Example Preview
Preview the first lines below, or expand to inspect the full source file.
import { useControllableState } from "@lattice-ui/core";
import React from "@rbxts/react";
type ControllableToggleProps = {
checked?: boolean;
defaultChecked?: boolean;
... import { useControllableState } from "@lattice-ui/core";
import React from "@rbxts/react";
type ControllableToggleProps = {
checked?: boolean;
defaultChecked?: boolean;
onCheckedChange?: (next: boolean) => void;
};
function ControllableToggle(props: ControllableToggleProps) {
const [checked, setChecked] = useControllableState({
value: props.checked,
defaultValue: props.defaultChecked ?? false,
onChange: props.onCheckedChange,
});
return (
<textbutton
AutoButtonColor={false}
BackgroundColor3={
checked ? Color3.fromRGB(53, 104, 196) : Color3.fromRGB(34, 41, 54)
}
BorderSizePixel={0}
Size={UDim2.fromOffset(220, 40)}
Text={checked ? "Controlled on" : "Controlled off"}
TextColor3={Color3.fromRGB(236, 240, 248)}
TextSize={14}
Event={{ Activated: () => setChecked((value) => !value) }}
>
<uicorner CornerRadius={new UDim(0, 8)} />
</textbutton>
);
}
export function CoreExample() {
const [checked, setChecked] = React.useState(true);
return (
<frame BackgroundTransparency={1} Size={UDim2.fromOffset(260, 120)}>
<ControllableToggle checked={checked} onCheckedChange={setChecked} />
<textbutton
AutoButtonColor={false}
BackgroundColor3={Color3.fromRGB(60, 76, 104)}
BorderSizePixel={0}
Position={UDim2.fromOffset(0, 56)}
Size={UDim2.fromOffset(220, 34)}
Text="Set uncontrolled fallback"
TextColor3={Color3.fromRGB(236, 240, 248)}
TextSize={14}
Event={{ Activated: () => setChecked(false) }}
>
<uicorner CornerRadius={new UDim(0, 8)} />
</textbutton>
</frame>
);
} Install
Global CLI command: lattice add core
Monorepo local script
Use your package manager wrapper when running the local lattice command.
pnpm lattice add corePublic Exports
-
createStrictContext -
FocusScopeProvider -
useFocusScopeId -
FocusLayerProvider -
useFocusLayerOrder -
registerFocusNode -
createFocusScopeId -
unregisterFocusNode -
registerFocusScope -
syncFocusScope -
unregisterFocusScope -
retainExternalFocusBridge -
releaseExternalFocusBridge -
canFocusNode -
focusNode -
focusGuiObject -
clearFocus -
getFocusedNode -
getFocusedGuiObject -
captureRestoreSnapshot -
restoreSnapshot -
useFocusNode -
getOrderedSelectionEntries -
isOrderedSelectionEntryAvailable -
findOrderedSelectionEntry -
getCurrentOrderedSelectionEntry -
getFirstOrderedSelectionEntry -
getRelativeOrderedSelectionEntry -
focusOrderedSelectionEntry -
React -
ReactRoblox -
setRef -
composeRefs -
Slot -
useControllableState
State Model
- There is no package-level runtime model;
coreprovides helper primitives that let callers build their own stateful components. useControllableStateis the main pattern bridge between controlled and uncontrolled usage across the package ecosystem.- Slot and ref helpers preserve caller-owned tree structure while still allowing behavior injection.
Key API
useControllableState
Use this when a primitive should support both controlled props and internal fallback state.
Slot / Slottable
Use slots when your package should attach behavior to caller-owned Roblox instances instead of forcing a host element.
composeRefs
Use composed refs to merge internal wiring with a caller-provided ref without dropping either one.
Composition Patterns
New headless primitives
Combine useControllableState, context helpers, and Slot when you want a package to look and behave like the rest of Lattice UI.
Interop with caller-owned hosts
Use the slot pattern when the app already has a visual button or frame and only needs interaction/state semantics applied to it.
Cautions / Limits
coreis intentionally low-level; if you only need styling, layout, or a finished headless primitive, preferstyle,system, or a component package.- Directly depending on
coreusually means you are authoring infrastructure, not just composing product UI.