Component API design is the contract between your UI component and the engineers who consume it. In frontend interviews, the answer is not just the JSX that renders; it is the React props, events, slots, state ownership, accessibility, styling, and escape hatches you choose not to expose.
Use this as a frontend component API design interview practice map. Start from a familiar component prompt, name the API contract, ship the MVP, then explain the trade-offs that would make the component scale in a team codebase.
Component API design patterns interviewers ask about
These prompts expose different API decisions: controlled state, composition, event payloads, a11y contracts, styling hooks, and when to keep the surface area small.
Modal / Confirm Dialog
API contract: open, defaultOpen, onOpenChange, title, description, actions.
Trade-off/test focus: controlled vs uncontrolled, focus restore, Escape policy, slots.
Tabs
API contract: value, defaultValue, onValueChange, item ids, disabled tabs.
Trade-off/test focus: roving tabindex, tabpanel linkage, controlled active value.
Accordion
API contract: type, value, defaultValue, collapsible, onValueChange.
Trade-off/test focus: single vs multiple sections, button semantics, disabled items.
Autocomplete / Combobox
API contract: inputValue, selectedValue, onInputChange, onSelect, renderOption.
Trade-off/test focus: async states, stale responses, keyboard selection, option identity.
Contact Form
API contract: field schema, validation timing, onSubmit(values), errors, disabled submit.
Trade-off/test focus: controlled fields, accessible errors, submit lifecycle.
Data Table / Pagination
API contract: rows, columns, page, pageSize, onPageChange, empty state.
Trade-off/test focus: derived rows, page bounds, cell renderers, table semantics.
Dynamic Table
API contract: column descriptors, accessors, header labels, fallback renderers, stable row keys.
Trade-off/test focus: typed columns, missing values, sort follow-up, prop explosion.
Nested Checkbox Tree
API contract: nodes, selected ids, onCheckedChange(ids), indeterminate state, disabled nodes.
Trade-off/test focus: parent-child sync, controlled selection, reset, traversal cost.
Star Rating
API contract: value, max, readOnly, onValueChange, labels.
Trade-off/test focus: hover preview vs committed value, keyboard input, a11y names.
Progress Bar
API contract: value, min, max, threshold colors, label, reduced motion.
Trade-off/test focus: bounded values, ARIA progressbar, theme hooks, animation policy.
Component API decision framework
Controlled vs uncontrolled
Name state ownership first: value/defaultValue/onValueChange for value state and open/defaultOpen/onOpenChange for visibility state.
- Controlled when parent coordinates validation, URL state, analytics, or reset.
- Uncontrolled when the widget is isolated and interview time is tight.
- Decision tree: if another component needs the state, lift it; otherwise keep it local and expose a controlled mode as follow-up.
Composition vs configuration
Prefer children, named slots, or compound components when content shape changes; use simple props when the API is stable.
- Prop explosion is a smell when every new layout needs another boolean.
- Compound components work well for Modal, Tabs, Accordion, and Menu prompts.
- Configuration is fine for small variants like
sizeandvariant.
Event naming and payloads
Use domain events instead of raw DOM events: onSelect({ value, item, reason }) is easier to consume and test than leaking click details.
- Name events by state change:
onValueChange,onOpenChange,onPageChange. - Include enough payload for analytics, undo, validation, or async follow-up.
- Keep the raw event optional unless the interviewer asks for it.
Styling surface
Expose variant, size, className, and CSS variables when they map to real design-system needs.
- Use enums for known visual choices; avoid many boolean styling props.
- Expose CSS variables for theme values like colors, radius, and spacing.
- Do not make every internal element individually styleable in the first pass.
Escape hatches
Mention ref, asChild, slotProps, and render hooks as follow-ups, not as the first API surface.
- Use
reffor focus and integration with forms or popovers. - Use
slotPropsonly when consumers need targeted internal element control. - Avoid over-abstracted state reducers unless the prompt asks for library-grade APIs.
Accessibility as API
Treat labels, ids, ARIA linkage, keyboard expectations, focus restore, and focus trap as part of the public contract.
- Expose labels and descriptions instead of hardcoding anonymous controls.
- State keyboard behavior before coding: Escape, Arrow keys, Enter, Tab order.
- Call out focus restore/trap policy for dialogs, popovers, and menus.
Worked examples: API contracts interviewers can score
Modal/Dialog API
Contract: open/defaultOpen/onOpenChange, title, description, slots for header/body/footer, focus restore, Escape policy, and an explicit a11y contract.
- Use controlled open state when parent owns destructive action flow.
- Use slots when footer actions vary across confirm, alert, and custom dialogs.
- Practice: Modal / Confirm Dialog.
Tabs API
Contract: controlled active value, item ids, onValueChange, disabled tab policy, roving tabindex, and linked tabpanel ids.
- Use stable string ids instead of array indexes when panels can reorder.
- Clarify whether disabled tabs are skipped by Arrow keys.
- Practice: React Tabs.
Autocomplete API
Contract: inputValue vs selected value, onInputChange, onSelect, async loading/error/empty states, stale response policy, and renderOption for custom rows.
- Separate typed text from committed selection so clearing and editing are predictable.
- Ignore stale responses or track request identity before rendering options.
- Practice: React Autocomplete.
type DialogProps = {
open?: boolean;
defaultOpen?: boolean;
onOpenChange?: (open: boolean, reason: 'trigger' | 'escape' | 'confirm' | 'cancel') => void;
title: string;
description?: string;
children: React.ReactNode;
};
type SelectEvent<T> = {
value: string;
item: T;
reason: 'keyboard' | 'pointer' | 'programmatic';
};45/60-minute component API round flow
| Time | Move | Signal |
|---|---|---|
| 0-5 min | Clarify consumers, data shape, ownership, keyboard expectations, and required customization. | You design for the caller, not just the demo. |
| 5-12 min | Define contract: props, events, slots, state ownership, accessibility labels, and styling surface. | Your API is explainable before implementation. |
| 12-30 min | Ship MVP behavior with the smallest API that supports the prompt. | You can implement without over-designing. |
| 30-45 min | Add a11y/keyboard, event payloads, controlled mode, disabled/error/loading states. | You know frontend correctness lives in contracts. |
| 45-60 min | Explain trade-offs, name follow-ups, and identify what you intentionally did not expose. | You can reason like a component owner. |
What interviewers score
- Clear contract: props, events, slots, and ownership are named before complexity grows.
- State ownership: controlled/uncontrolled decisions match the caller and prompt constraints.
- Event payloads: domain payloads carry useful data and reasons without leaking internals.
- A11y contract: labels, ids, ARIA linkage, keyboard paths, and focus behavior are explicit.
- Styling surface: variants, size, className, and CSS variables are enough without exposing everything.
- Trade-off narration: you can name the API you skipped and why it was not needed yet.
What to skip vs prioritize
| Priority | Do this | Avoid spending time on |
|---|---|---|
| Prioritize | Clear contract, state ownership, event payload, a11y, styling surface, and a dry run. | Inventing a generic component library before the prompt works. |
| Know lightly | Compound components, slot props, render hooks, refs, and escape hatches as follow-ups. | Over-abstracted state reducers in the first implementation pass. |
| Skip unless asked | Theme engines, polymorphic components, portals, virtualization, and global config systems. | Excessive escape hatches that make the API harder to explain. |
Choose your next practice format
FAQ
What is component API design?
Component API design is the way you define how another engineer consumes your component: props, events, slots, state ownership, accessibility labels, styling hooks, and escape hatches.
How do I practice component API design for frontend interviews?
Pick a real prompt like Modal, Tabs, Autocomplete, or Data Table. Name the API contract first, then implement the MVP, keyboard support, a11y contract, styling surface, and trade-offs.
What is the controlled vs uncontrolled component API pattern?
Say who owns the state. Controlled components receive state and callbacks from the parent; uncontrolled components manage local state but can expose defaults and change events.
When should I use compound components or slots?
Use compound components or slots when the content shape varies, such as Modal, Tabs, Accordion, Menu, or Table prompts. Use simple props for stable, small choices.
How should component APIs expose events and accessibility?
Prefer domain payloads like onSelect({ value, item, reason }) instead of raw DOM events. Accessibility is part of the public API too: labels, ids, ARIA relationships, keyboard behavior, visible focus, and focus restore should be named in the contract.