A Button API: variants, as, asChild, className, rest and ref
Read the printed HTML. as="a" changed the tag; asChild produced one <a> with the button classes merged into the link's own class; the caller's wide was added to, not swapped for, the button classes; aria-label, data-testid and disabled passed through ...rest; and the ref reached the real <button>.
A component's props are its API: name them like the platform (onChange, disabled), support both controlled (value + onChange) and uncontrolled (defaultValue) use, prefer composition with children over piles of configuration props, express looks as variant/size, and leave escape hatches: className, ...rest and ref.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 5 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write a Toggle (a switch button) that supports checked, defaultChecked and onCheckedChange, using useControllableState.
Turn this configuration API into a composition API: <Alert title="Saved" message="Your draft was saved." actionLabel="Undo" onAction={undo} />.
Explain it without notes
How would you design a component that can be both controlled and uncontrolled?
What does "composition over configuration" mean for component APIs? Give an example.
What is the difference between an as prop and the asChild pattern?
Which escape hatches should a design-system component offer, and why?
Preview
Console
Nothing logged yet.Expected console output
<button class="btn btn-primary btn-md" type="button">Save</button>
<button class="btn btn-danger btn-sm" type="button" aria-label="Delete draft">Delete</button>
<a class="btn btn-secondary btn-md" href="#docs">Docs</a>
<a href="#help" class="btn btn-secondary btn-md with-icon">Help</a>
<button class="btn btn-primary btn-md wide" type="button" data-testid="buy" disabled="">Buy</button>
Focused: Save