Primitive
Unstyled base element that renders any native HTML element with optional asChild composition.
Primitive
The Primitive component is the foundation of all unstyled, headless UI components in @loke/ui. It provides a consistent way to render any native HTML element (div, button, span, etc.) with support for the asChild pattern.
Primitive is rarely used directly. You use it internally when building custom components, and you interact with it through other primitives (Button, Dialog, etc.).
Features
- Renders any native HTML element (div, button, span, a, form, img, svg, etc.)
- Supports asChild to merge props with a child element via Slot
- Forwards refs correctly
- Passes through all native HTML attributes
Usage
import { Primitive } from '@loke/ui/primitive';
// Render a div
<Primitive.div className="my-component">Content</Primitive.div>
// Render a button
<Primitive.button onClick={handleClick}>Click me</Primitive.button>
// Use asChild to render a custom element with Primitive props
<Primitive.button asChild>
<a href="/page">Link that looks like a button</a>
</Primitive.button>Props
Prop
Type
Available Elements
Primitive supports all common HTML elements:
Primitive.a
Primitive.button
Primitive.div
Primitive.form
Primitive.h2
Primitive.h3
Primitive.img
Primitive.input
Primitive.label
Primitive.li
Primitive.nav
Primitive.ol
Primitive.p
Primitive.select
Primitive.span
Primitive.svg
Primitive.ulExamples
Primitive.p — renders a <p>
Basic element rendering
<Primitive.div className="p-4 border rounded">
Unstyled div with custom classes
</Primitive.div>Form elements
<Primitive.label htmlFor="email">Email</Primitive.label>
<Primitive.input id="email" type="email" />asChild pattern
// Without asChild: extra wrapper
<Primitive.button onClick={onClose}>
<span>Close</span>
</Primitive.button>
// With asChild: no wrapper, props merged onto child
<Primitive.button asChild>
<span onClick={onClose}>Close</span>
</Primitive.button>How asChild works
When asChild is true:
- Primitive uses Slot to merge its props with the child element
- Handlers (onClick, onChange, etc.) are composed together
- Class names are merged (both applied)
- Styles are merged
- Refs are forwarded to the child
This allows you to render a custom element while keeping Primitive's behavior (proper prop passing, ref forwarding, handler composition).
Accessibility
Primitive is unstyled and has no built-in accessibility features. Ensure:
- Semantic HTML is used (button for buttons, a for links, etc.)
- ARIA attributes are applied correctly (
role,aria-label,aria-expanded, etc.) - Keyboard interactions are implemented (Tab, Enter, Escape, Arrow keys as appropriate)
Best practices
- Use Primitive internally in component libraries; rarely use directly in applications
- Prefer semantic HTML elements over divs (button, a, label, etc.)
- Don't set
asChild={true}on non-interactive elements rendered as interactive (e.g., div as button without proper ARIA/keyboard handling) - Remember that Primitive adds no styling; styling is the responsibility of the consuming code