Own the markup

Take behavior from a library, and write the markup yourself.

Adam Wathan@adamwathan
It's *so* much easier to build the right markup yourself than it is to try and hack your design into the wrong starting point. I end up rebuilding every simple UI component from scratch for every project; this approach finally gives me hope I won't have to ๐Ÿ‘๐Ÿป
Dec 18, 2017View on X โ†—
Adam Wathan@adamwathan
@kentcdodds Downshift's approach of handling all of the quirky logic for you while deferring all of the markup generation to the end user is so much more developer-friendly, even if it initially looks like more work.
Dec 18, 2017View on X โ†—

Both panes share one select controller. Only the markup differs.

Your markup

Assign an owner

Open both, use the arrow keys, then swap your markup.

Fixed markupBad
options: string[]
HeadlessGood
renderOption(item)
const select = useSelect({ items, onChange });

<button {...select.trigger}>
  {select.value ? <Person p={select.value} /> : "Pick a person"}
</button>
<ul {...select.list}>
  {items.map((p, i) => (
    <li {...select.option(i)}><Person p={p} /></li>  // your markup
  ))}
</ul>

Why it matters, pros and cons

Why it matters

Keyboard, focus and ARIA are the hard part. The markup is the part your design changes.

Pros

  • Any layout is reachable: avatars, badges, tiles.
  • Behavior is written and tested once.
  • No overriding another library's class names.

Cons

  • More code up front than a drop-in widget.
  • You must wire the props onto the right elements.
  • For a plain native select, use the native select.