5.6 KiB
API Design Conventions
Prop naming, defaults, and documentation patterns for consistent component APIs.
Reference implementations: Base UI, Radix, Ark UI
Prop Naming
Boolean Props
Use positive adjectives, avoid is/has prefixes:
| ✅ Good | ❌ Avoid |
|---|---|
disabled |
isDisabled |
required |
isRequired |
open |
isOpen |
loading |
isLoading |
State Props
| State | Uncontrolled | Controlled | Handler |
|---|---|---|---|
| Open | defaultOpen |
open |
onOpenChange |
| Value | defaultValue |
value |
onValueChange |
| Checked | defaultChecked |
checked |
onCheckedChange |
| Selected | defaultSelected |
selected |
onSelectedChange |
Event Handlers
Pattern: on + Noun + Verb
| ✅ Good | ❌ Avoid |
|---|---|
onOpenChange |
handleOpen, setOpen |
onValueChange |
onChange (too generic) |
onSelect |
onItemSelected |
onDismiss |
onClose (ambiguous) |
Standard Props by Category
Interaction
disabled?: boolean;
required?: boolean;
readOnly?: boolean;
autoFocus?: boolean;
Collections
multiple?: boolean; // Allow multiple selection
loopFocus?: boolean; // Arrow keys loop at ends
orientation?: 'horizontal' | 'vertical';
typeahead?: boolean; // A-Z navigation
Popups
modal?: boolean | 'trap-focus';
closeOnEscape?: boolean;
closeOnOutsideClick?: boolean;
keepMounted?: boolean; // Keep in DOM when closed
Positioning
side?: 'top' | 'bottom' | 'left' | 'right';
align?: 'start' | 'center' | 'end';
sideOffset?: number;
alignOffset?: number;
collision?: 'flip' | 'shift' | 'none';
Change Event Details
What: Rich context passed to change handlers.
Why: Enables conditional logic — prevent changes, track analytics, debug.
interface ChangeDetails {
reason: string; // 'click' | 'keyboard' | 'blur' | 'escape' | 'outside-click'
event?: Event; // Original DOM event
cancel(): void; // Prevent internal state change
}
Usage: onOpenChange={(open, details) => { if (details.reason === 'outside-click') details.cancel(); }}
Imperative Actions
What: Expose actions via actionsRef for programmatic control.
Common actions:
open(),close(),toggle()— Popupsfocus()— Focus managementscrollToIndex(i)— Virtualized lists
Pattern: <Dialog.Root actionsRef={ref}> → ref.current.open()
Render Delegation
The render Prop
What: Replace default element while preserving behavior.
Forms:
- Element:
render={<a href="..." />}— Props merged onto element - Function:
render={(props, state) => ...}— Full control
className / style as Function
What: State-aware styling without external state.
Pattern: className={(state) => state.checked ? 'on' : 'off'}
Defaults
Sensible Defaults (80% case)
| Prop | Default | Rationale |
|---|---|---|
side |
'bottom' |
Most common popup position |
align |
'center' |
Visually balanced |
sideOffset |
8 |
Standard spacing |
closeOnEscape |
true |
Expected behavior |
loopFocus |
true |
Better keyboard UX |
Require Explicit Opt-in
| Prop | Default | Rationale |
|---|---|---|
autoFocus |
false |
Can be disorienting |
modal |
false |
Has side effects (scroll lock) |
keepMounted |
false |
Performance |
Escape Hatches
| Need | Solution |
|---|---|
| DOM access | Forward refs to root element |
| Custom attributes | Spread ...props |
| Custom portal target | container prop on Portal |
| Override handlers | Spread after internal handlers |
Documentation Pattern
Props Table
| Prop | Type | Default | Description |
|---|---|---|---|
open |
boolean |
— | Controlled open state |
defaultOpen |
boolean |
false |
Initial open state |
onOpenChange |
(open, details) => void |
— | Called on state change |
Data Attributes Table
| Attribute | When Present |
|---|---|
data-open |
Component is open |
data-disabled |
Component is disabled |
Anatomy Section
Show component structure with all parts.
Examples Section
Basic usage, controlled, with custom trigger, etc.
Versioning
| Change | Breaking? |
|---|---|
| Add optional prop | No |
| Change default value | Yes |
| Remove prop | Yes |
| Add required prop | Yes |
Deprecation Pattern
- Add new prop alongside old
- Log warning when old prop used
- Remove old prop in next major
Checklist for New Components
- Boolean props use positive adjectives
- State props follow
value/defaultValue/onValueChange - Change handlers receive
detailswithreasonandcancel() - Ref forwarded to root DOM element
renderprop for element polymorphismclassNameaccepts function- State exposed via
data-*attributes - Additional HTML attributes spread
- Types exported (
Component.Props,Component.State)
References
| Library | API Style |
|---|---|
| Base UI API | Canonical reference |
| Radix API | Alternative conventions |
| Ark UI API | Cross-framework |