Getting Started
Install
npm i @spacing-ui/core
# or
pnpm add @spacing-ui/core
# or
yarn add @spacing-ui/coreReact 18 or 19 is required (peer dependency).
Your first component
Every primitive is headless — it renders semantic HTML with the correct ARIA attributes, and you style it however you want.
import { Dialog } from "@spacing-ui/core";
export function Example() {
return (
<Dialog>
<Dialog.Trigger>Open dialog</Dialog.Trigger>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Title>Hello</Dialog.Title>
<Dialog.Description>This is a headless dialog.</Dialog.Description>
<Dialog.Close>Close</Dialog.Close>
</Dialog.Content>
</Dialog>
);
}That’s it. No styles are applied — Dialog handles the focus trap, escape-to-close, body scroll lock, and ARIA wiring. You handle the look.
Controlled vs uncontrolled
Every stateful primitive supports both patterns. Pass defaultOpen / defaultValue for uncontrolled, or pass open / value with the matching onOpenChange / onValueChange for controlled.
// Uncontrolled
<Dialog defaultOpen>...</Dialog>
// Controlled
const [open, setOpen] = useState(false);
<Dialog open={open} onOpenChange={setOpen}>...</Dialog>Compound components
All primitives use the compound pattern. Sub-parts are namespaced on the root:
<Tabs defaultValue="a">
<Tabs.List>
<Tabs.Trigger value="a">A</Tabs.Trigger>
<Tabs.Trigger value="b">B</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="a">Panel A</Tabs.Content>
<Tabs.Content value="b">Panel B</Tabs.Content>
</Tabs>What’s next
- Browse the component reference
- Read the theming guide to see recipes for CSS variables, Tailwind, and CSS Modules
- Skim keyboard shortcuts for a summary of the interaction model
Last updated on