Build a drag and drop reorder list
Design the behavior contract for a drag and drop reorder list. Focus on state, keyboard interaction, empty/loading/error states, and how the component composes with product data.
Answer Strategy
For drag and drop reorder list, answer as an interaction contract. Name the role, accessible name, keyboard map, focus behavior, controlled state, and async or empty states before discussing pixels.
Build the behavior as a headless primitive when multiple product surfaces will reuse it. Visual styling can wrap that primitive, but focus and keyboard semantics should not be rewritten per screen.
The reference implementation shows the pattern interviewers look for: controlled value, explicit active index, roving focus or active descendant, pointer handling that does not steal focus, and tests that query the UI like a user.
Reference Implementation: Accessible Interaction Primitive For drag and drop reorder list
Adapt this listbox-style primitive for comboboxes, command palettes, menus, pickers, and selection-heavy UI questions. The sandbox below lets you run the browser interaction directly.
type Option = { id: string; label: string };
function useListbox(options: Option[], onSelect: (option: Option) => void) {
const [activeIndex, setActiveIndex] = React.useState(0);
function onKeyDown(event: React.KeyboardEvent) {
if (event.key === 'ArrowDown') {
event.preventDefault();
setActiveIndex((index) => Math.min(index + 1, options.length - 1));
}
if (event.key === 'ArrowUp') {
event.preventDefault();
setActiveIndex((index) => Math.max(index - 1, 0));
}
if (event.key === 'Enter') {
event.preventDefault();
const option = options[activeIndex];
if (option) onSelect(option);
}
}
return { activeIndex, onKeyDown };
}Executable UI Sandbox
UI interview practice should behave like component documentation, not a static snippet. This uses the same isolation pattern as Storybook, Sandpack, CodeSandbox, and StackBlitz: editable source on one side, a sandboxed browser preview on the other. Edit the DOM code, run it, and verify focus, keyboard, pointer, and state behavior in the preview.
Testing Strategy
Convert the answer into observable behavior. In a mid-senior interview, say which behaviors are covered by unit tests, interaction tests, accessibility checks, and one browser smoke path.
test('keyboard selection moves and commits active option', async () => {
const user = userEvent.setup();
render(<ExampleListbox />);
await user.keyboard('{ArrowDown}{Enter}');
expect(screen.getByRole('status')).toHaveTextContent('selected option 2');
});Interviewer Signal
Shows whether you can build components as interaction systems rather than visual boxes.
Constraints
- Name the controlled and uncontrolled state.
- Define keyboard and focus behavior.
- Include loading, empty, disabled, and error states.
Model Answer Shape
- Start with the accessibility role and interaction contract.
- Separate rendering slots from state management.
- Expose callbacks that describe user intent, not internal implementation details.
Tradeoffs
- A headless primitive is reusable but slower to consume.
- A product-specific component ships faster but can trap behavior in one use case.
Edge Cases
- Focus after close, selection, deletion, or route change.
- Large datasets and slow network responses.
- Screen reader labels and live updates.
Testing And Proof
- Keyboard path through the primary workflow.
- A11y names, descriptions, and roles.
- State transition after slow or failed data load.
Follow-Ups
- How would this component be documented in a design system?
- What props would you refuse to expose?