Customizing React-Select with TypeScript: A Developer’s Alchemy

Mihai MarinescuMihai Marinescu
5 min read

If you're reading this article because you think customizing the react-select library with TypeScript is going to be a walk in the park—close this tab right now. After combing through the documentation, I, too, was under the impression that this would be a plug-and-play endeavor. Man, was I in for a surprise!

Still here? Cool, let's dive right in.

The Magic Behind React-Select

For those who don't know, react-select is a highly customizable replacement for the HTML <select> element. It works exceptionally well with React, providing numerous ways to style the dropdown and the options within.

How To Create Custom Options with HTML Templates

Before diving into the code, let's understand that react-select gives you a very nifty components prop. This allows you to override any part of the UI.

Here's a quick example:

import React from 'react';
import Select, { components } from 'react-select';

const CustomOption = (props: any) => (
  <components.Option {...props}>
    <div>
      <strong>{props.data.label}</strong>
      <small>{props.data.description}</small>
    </div>
  </components.Option>
);

export const MySelect = () => (
  <Select
    components={{ Option: CustomOption }}
    options={[
      { label: 'Apple', value: 'apple', description: 'A red fruit' },
      { label: 'Banana', value: 'banana', description: 'A yellow fruit' }
    ]}
  />
);

If you want to add more finesse to your options, you can use the isDisabled, isFocused, isSelected props.

import React from 'react';
import Select, { components } from 'react-select';

const CustomOption = (props: any) => {
  const { isDisabled, isFocused, isSelected } = props;

  return (
    <components.Option {...props}>
      <div style={{ opacity: isDisabled ? 0.5 : 1 }}>
        <strong>{props.data.label}</strong>
        { isSelected && <span></span> }
        <small style={{ color: isFocused ? 'blue' : 'grey' }}>
          {props.data.description}
        </small>
      </div>
    </components.Option>
  );
};

export const MySelect = () => (
  <Select
    components={{ Option: CustomOption }}
    options={[
      { label: 'Apple', value: 'apple', description: 'A red fruit' },
      { label: 'Banana', value: 'banana', description: 'A yellow fruit' }
    ]}
  />
);

Now you can make that option look the way you want.

Styling the Menu and MenuList: Giving Your Dropdown Some Sass

So you know:

  • the menu: is essentially the container that wraps around all the options you see when the dropdown is activated. It is responsible for the placement, size, and other container-specific styling of the dropdown list. Customizing menu will affect the dropdown's outer box.

  • the menuList: is a child component within the menu and directly wraps around the individual option items. Customizing menuList will affect the styling and layout of the options inside the menu but not the external container itself.

Think of menu as the outer shell and menuList as the inner lining. Any customization you apply to these components will alter how the dropdown appears and behaves when opened.

Customizing the style in react-select is made possible with the styles prop. You can easily change the border radius, color, and many other styles.

const customStyles = {
  menu: (provided: any) => ({
    ...provided,
    borderRadius: '0px',
    backgroundColor: 'lightgray'
  }),
  menuList: (provided: any) => ({
    ...provided,
    padding: '0px'
  })
};

export const MyStyledSelect = () => (
  <Select
    styles={customStyles}
    options={[
      { label: 'Apple', value: 'apple' },
      { label: 'Banana', value: 'banana' }
    ]}
  />
);

Just remember that some of the styles might need an !important next to them or else they won't show.

Let's Jazz Up That Input

The input box itself can also be styled, just like the menu and options. Simply target control in the styles prop.

const inputStyles = {
  control: (provided: any) => ({
    ...provided,
    borderRadius: '15px',
    borderColor: '#3f51b5'
  })
};

export const MyInputStyledSelect = () => (
  <Select
    styles={inputStyles}
    options={[
      { label: 'Apple', value: 'apple' },
      { label: 'Banana', value: 'banana' }
    ]}
  />
);

Don't forget about the menuPosition

The menuPosition is another vital setting when you're customizing your dropdowns, and it's often mistaken for menuPlacement. While menuPlacement determines the location of the menu in terms of above or below the control, menuPosition goes further to dictate how the dropdown menu is positioned in the DOM.

Here are the options you can set for the menuPosition prop:

  • absolute: This is the default value. The menu is rendered within the DOM hierarchy of the control. It won't break out of a parent element with overflow: hidden set. So, if your react-select component is inside a container that hides overflow, the dropdown might not be displayed properly.

  • fixed: The menu is rendered at the root level of the DOM hierarchy, so it's not constrained by a parent's overflow: hidden style. This setting allows the dropdown to break out of any containing element, ensuring visibility.

Here's how you'd set it in code:

import Select from 'react-select';

export const MyFixedMenuSelect = () => (
  <Select
    menuPosition="fixed"
    options={[
      { label: 'Apple', value: 'apple' },
      { label: 'Banana', value: 'banana' }
    ]}
  />
);

In this example, by setting the menuPosition to "fixed", the dropdown will be appended to the root level of the DOM and will not be confined by a parent element with overflow: hidden.

Think of menuPosition as the setup for how your dropdown behaves in a broader context within the DOM, much like how middleware affects an entire application. It gives you the control to adapt your dropdown menu to various scenarios and layout constraints. Choose wisely based on your application's needs!

The Aha Moment

By now, you should be feeling pretty comfortable manipulating react-select with TypeScript.

The best part? You can have these complex styles and custom options up and running faster than you can say "JavaScript fatigue."

That’s all, folks.

Happy coding! 🚀

0
Subscribe to my newsletter

Read articles from Mihai Marinescu directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mihai Marinescu
Mihai Marinescu