Skip to contentSkip to footer

If you want to be kept up to date with new articles, CSS resources and tools, join our newsletter.

Selectlist is an upcoming element that replaces the native select element with a fully stylable and customizable one, while still keeping all the native interactions and accessibility features. It's a sorely needed improvement and will let you do away with all your custom select implementations.

Selectlist is an experimental feature. It's still being worked on and you can't use it in browsers yet, unless you use Polypane (...or a Chromium based browser where you have experimental features turned on.)

This isn't a full exploration of what selectlist is and can do, but a quick explainer of how to get selectlist to play nicely with React. We ran into issues while replacing some of the select elements in the Polypane UI, which is built with React, and we hope this prevents you from running into the same issues. For a nice introduction, check out Hidde's article Two levels of customising <selectlist> and for a full explainer, head over to OpenUI which is where the element is being developed.

Select in React

When you want to do something when the value in your regular select changes in th regular React way, you use the onChange prop:

<select onChange={(e) => doSomething(e)}>
  <option value="hex">Hex</option>
  <option value="rgb">Rgb</option>
  <option value="hsl">HSL</option>
</select>

And every time a user picks a new option, the onChange is fired and you can handle whatever you need to do with the event.

Selectlist in React

To "migrate" from select to selectlist, you'd expect to be able to just swap them out:

<selectlist onChange={(e) => doSomething(e)}>
  <option value="hex">Hex</option>
  <option value="rgb">Rgb</option>
  <option value="hsl">HSL</option>
</selectlist>

Unfortunately, that's not the case. When you do that you'll notice that while the selectlist value updates, onChange doesn't fire. That's because the onChange prop is a React concept, not a different notation for the DOM native change event.

React has specific code that calls the onChange function whenever the change event fires on select elements, but it doesn't know about selectlist yet so the onChange on selectlist doesn't get called.

To make it work, we need to add the listeners ourselves using useEffect and useRef:

import { useEffect, useRef } from 'react';

const OurComponent = () => {
  const selectlist = useRef();

  useEffect(() => {
    const currentSelectlist = selectlist.current;
    currentSelectlist.addEventListener('change', doSomething);

    return () => {
      currentSelectlist.removeEventListener('change', doSomething);
    };
  }, []);

  function doSomething(e) {
    // do something with the event
  }

  return (
    <selectlist ref={selectlist}>
      <option value="hex">Hex</option>
      <option value="rgb">Rgb</option>
      <option value="hsl">HSL</option>
    </selectlist>
  );
};

We use a ref to get the actual DOM element of the selectlist, and then use useEffect to add (and remove) event listeners to it manually. The listener doesn't need to change when we update the element, so we add an empty array as the dependency array of the useEffect.

To prevent event listeners from hanging around and taking up memory after we've removed the element from the DOM, we also add a return function to the useEffect to remove the event listener when the component unmounts.

Selectlist as a separate React component

You probably don't want to add that useEffect to each component you use a Selectlist in, so instead you can extract it into its own component:

import { useEffect, useRef } from 'react';

const Selectlist = ({ onChange, children, value }) => {
  const ref = useRef();

  useEffect(() => {
    const selectlist = ref.current;
    selectlist.addEventListener('change', onChange);
    return () => selectlist.removeEventListener('change', onChange);
  }, [onChange]);

  return (
    <selectlist ref={ref} value={value}>
      {children}
    </selectlist>
  );
};

// and then use it like this:
const OurComponent = () => {
  // do your domain specific stuff

  return (
    <div>
      {/* your other component parts */}
      <Selectlist onChange={doSomething} value={value}>
        <option value="hex">Hex</option>
        <option value="rgb">Rgb</option>
        <option value="hsl">HSL</option>
      </Selectlist>
    </div>
  );
};

And that's how you can use selectlist in React.

Keep in mind that selectlist isn't something you can use in the wild and have work for your visitors, and that it's still in development so things might change.

We have the luxury of determining our own browser support and we're happily using it inside Polypane to improve how our Selects work without sacrificing on accessibility or existing user expectations. If you want to play around with selectlist too, try Polypane for free.

Build your next project with Polypane

  • Use all features on all plans
  • On Mac, Window and Linux
  • 14-day free trial – no credit card needed
Try for free
Polypane UI