Custom React Dropdown With Native Mobile Behavior
Customize your <select> elements without reinventing the wheel
This post is for you if you’ve ever asked yourself one of these questions:
- How to open the
select
dropdown menu programmatically - How to style the
select
element? - How to make a custom element preserve native
<select>
tag behavior? - How to use custom icons with the
select
element?
What?
I will show you how to make any element into a select
with native behavior but completely custom styling and contents.
Why?
The select element is notoriously hard to style, especially in a way that works across browsers. If you also want to include custom elements inside, like icons, it becomes even harder.
You could implement your own select element with custom logic, but for most applications that’s unnecessarily complex. Dropdown menus don’t work very well on mobile devices, which is why mobile browsers render them as fullscreen overlays. This adds additional complexity to any custom component implementation, as you need to handle two different display modes.
Simply using a label
or dispatching events like: click
, keydown
, change
doesn’t work. It will only focus the element. (dispatching the click event might work in Chrome, but this method should not be used anyway, as it is considered deprecated in Chrome 53+). It turns out that triggering the select
dropdown programmatically is impossible. So what now?
How?
- Create a container with a
select
andlabel
inside it (It is important to put the label after the select, I’ll explain why in a second) - Give the container
position: relative
and define its dimensions - Give the select
position: absolute
and stretch it out to cover the entire container by settingwidth:100%; height:100%; top:0; left:0;
- Hide the select by giving it
opacity: 0
- Using this selector
selector:focus + label
you can style the focused state, this is also why thelabel
has to come after theselect
This way we are able to display anything we want as the clickable component and still keep the native behavior. Do you want to show an animated loader to indicate options being loaded from the server? Done. Do you want to use a third party icon library? Done. Do you want to use your own SVGs? Also done.
If you want to change the content or appearance of your component, based on which option is selected, just turn it into a controlled component and modify the children based on the state.
Here is the code, feel free to play around with it:
Accessibility
The select tag is technically still there so you can still focus it with your keyboard and with some clever CSS you are also able to style the :focus
or :hover
state. Wrapping the custom element in the label
tag should aid screen-readers in knowing what the select
is for.
Alternatives
- If you need full control and are ready to take on the complexity, you should probably implement your own Select component from the ground up.
- If you only want to change the dropdown icon and you don’t mind using an image, you could try using the background-image method. Check out this example:
If this guide helped you consider leaving a few claps 👏 If something was unclear, or you would like me to expand on some topic, let me know in the comments 💬 All constructive criticism is welcome.