The best components disappear — you reach for one, it does the obvious thing, and you move on without opening the docs. The worst make you stop and think: twenty props, half of them required, none named the way you’d guess. The gap between them is API design, and it comes down to a few conventions applied without exception.
Compose, don’t configure
The instinct is to add a prop for every variation. A card sprouts headerTitle, headerSubtitle, showFooter, footerAlign, and collapses under its own options. Every new design becomes another boolean, until the type signature is a wall nobody reads.
Composition is the way out: expose the parts. Ship Card, CardHeader, CardContent, and CardFooter and let people arrange them. You write less, and the layout they need but you never imagined is just JSX.
Name variants the same way
Pick a vocabulary and use it in every component. variant for visual style — default, ghost, destructive. size for scale — sm, default, lg. Two names, learned once, applied to forty components.
Consistency is the whole game here. When Button, Badge, and Alert all take the same variant names, people guess right the first time and stop reading prop tables.
Leave escape hatches
You won’t predict every use, so build the exits in from the start. Two cover almost everything:
className, merged with the component’s own classes via tailwind-merge, so a caller can adjust one edge case without forking.asChild, which renders your component as its child element through Radix Slot — so aButtoncan become a Next.jsLinkwithout duplicating a single style.
This is why copy-paste libraries feel so flexible. Spectrum UI wires className and asChild in by default, so the hatch is there before you reach for it. You inherit the flexibility without having to design for it.
The rule
Design the common case to need zero props, and make the rare case possible without a fork. If someone can render your component with <Button>Save</Button> and also bend it to a one-off without editing your source, the API is right.
Ship that, and people stop rebuilding what you already gave them — which is the only metric a component API really has. The best compliment yours gets is silence: no issues, no forks, no Slack thread asking how it works.