gentleduck/primitives
Unstyled, accessible React primitives for building serious component libraries and product-grade interaction systems.
gentleduck/primitives gives you production behavior without visual opinions. You own design tokens and styling; primitives own accessibility, focus, dismissal, and interaction semantics.
What this package is
gentleduck/primitives is a set of low-level React building blocks for teams that need:
- Correct keyboard and screen reader behavior.
- Flexible composition (
asChild, slotting, scoped contexts). - Controlled and uncontrolled state strategies.
- Animation-aware mount/unmount semantics.
- A clear path from raw primitive to polished design-system component.
This package is not a CSS framework. It is an interaction and accessibility layer.
When to use primitives
Use primitives when you are building:
- A reusable design system across many apps.
- Product surfaces with complex overlays (dialogs, popovers, menus).
- UI with strict accessibility requirements.
- Highly customized visuals where pre-styled libraries get in the way.
Do not use primitives if you only need quick, fixed styling with minimal customization. In that case, use pre-styled components instead.
Capability model
| Layer | Responsibility | Typical primitives |
|---|---|---|
| Foundation | Rendering abstraction and composition | Primitive Elements, Slot, Direction |
| Interaction infra | Focus, dismissal, mounting, positioning | Focus Scope, Dismissable Layer, Presence, Portal, Popper |
| Product patterns | Overlays, menus, inputs, selection | Dialog, Popover, Menu, Select, Slider |
This layering is the core reason primitives scale well in large codebases.
How teams usually adopt
- Start with one high-value wrapper (
Dialog,Popover,Menu). - Standardize wrapper rules:
forwardRef,classNamemerge, sensible defaults, no hidden primitive props. - Add design tokens and motion classes via
data-state. - Document wrapper contracts in your design-system package.
- Expand to additional primitives after keyboard + screen reader tests pass.
Avoid copying examples ad hoc into apps. Build wrappers once, then consume wrappers everywhere.
Architecture snapshot
Higher-level components are built by composition, not inheritance. You can always drop one level lower when you need custom behavior.
Production checklist
Before shipping primitive-based wrappers:
- Verify title/description semantics for dialogs.
- Verify keyboard loops and escape behavior.
- Verify focus restoration after close.
- Verify click-outside handling for nested overlays.
- Verify reduced-motion behavior.
- Verify RTL behavior if your app supports Arabic/Hebrew/Persian.
Internal examples
These are production-style primitive patterns from the internal registry.
1) Guarded async dialog
2) Side-aware popover
3) Dropdown menu selection semantics
4) Tooltip delay behavior
5) Controlled select
6) Alert dialog confirmation
Learn next
- Getting Started — Set up a production-ready baseline.
- Core Concepts — Understand internals and composition patterns.
- API Reference — Detailed props/events for every primitive.
- Course — Structured path from fundamentals to system-level patterns.