A headless useTabs hook with prop getters, two skins
The hook knows nothing about buttons, pills or colours. It hands out ids, roles, aria-*, a roving tabIndex, a ref and keyboard handlers; each skin spreads them onto its own elements. Click a tab, then use the arrow keys.
Put what the component does (state, data loading, rules) in a custom hook or a plain function, and how it looks in presentational components that only take props; headless components push this furthest by shipping behaviour and accessibility with no markup at all, and both halves become easy to test and reuse.
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.
Split this component: function Clock() { const [now, setNow] = useState(new Date()); useEffect(() => { const t = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(t); }, []); return <p className="clock">{now.toLocaleTimeString()}</p>; }
Write a headless useDisclosure({ defaultOpen }) hook that returns open, toggle, getButtonProps() and getPanelProps() with the right ARIA.
Explain it without notes
Why separate logic from UI in React? What do you gain?
What is a headless component, and why do design systems use them?
How would you test a component whose logic lives in a custom hook?
When is splitting logic from UI not worth it?
Preview
Console
Nothing logged yet.Expected console output
Focused tab: Specs | aria-selected: true
Visible panel: Size, weight and battery.
Pill skin starts on: Reviews