The escape hatch — a bare polymorphic element with no visual opinion. Exists so a consumer has something to attach a custom property or a one-off style to without reaching for a raw <div>.
primitiv add box installs it whichever mode you are reading.Playground
Preview
import { Box } from "@/components/ui/box";
<Box style={{ padding: "var(--primitiv-space-space-16)", border: "1px dashed var(--primitiv-border-default)", borderRadius: "var(--primitiv-radii-8)", }}> A plain box you style yourself</Box>Density is set by a data-density ancestor — the Context system, not a Box prop.
Installation
npx primitiv add boxpnpm dlx primitiv add boxyarn dlx primitiv add boxbunx primitiv add boxImport
import { Box } from "@/components/ui/box";No headless primitive — this one ships only as a copied styled surface, so primitiv add is the only way in, whichever mode you are reading.
Props
Box
Extends HTMLDivElement — every native attribute of that element is accepted and forwarded.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | — | Render the single child element instead of a wrapping <div>, merging the
box class onto it — e.g. <Box asChild><section>...</section></Box>. |
Styling contract
Accessibility
- Box renders a plain
<div>with no role, label or semantics of its own — correct for a pure styling wrapper, which should be invisible to assistive tech. - When the wrapper should mean something, use
asChildto render a real element (<section>,<nav>,<article>) rather than nesting a bare<div>: a landmark a screen-reader user can navigate to beats an anonymous box. - Any
roleoraria-*you pass forwards to the rendered element — Box adds none of its own, so it never fights or duplicates the semantics you give it.
Examples
As your own element
asChild renders the child element instead of a wrapping <div>, merging Box onto it. Reach for it when the wrapper should carry meaning — a <section>, <article> or <nav> — so you get a real landmark rather than an anonymous <div> that adds nothing to the accessibility tree.
import { Box } from "@/components/ui/box";
<Box asChild> <section aria-label="Account summary">{/* ... */}</section></Box>Scoping a design token
Because a design token is a CSS custom property, setting one on a Box re-points it for everything inside — the token cascades, so descendants that resolve --primitiv-space-space-16 inherit the new value here without prop-drilling. This is the canonical Box use: a subtree-scoped override with somewhere to attach it that is not a raw <div>.
import { Box } from "@/components/ui/box";
<Box style={{ "--primitiv-space-space-16": "2.5rem" }}> {/* Descendants resolving the token pick up the scoped value. */} <article style={{ padding: "var(--primitiv-space-space-16)" }}> {/* ... */} </article></Box>