DEV Community

Cover image for React Select with options outside
Richard Beattie
Richard Beattie

Posted on • Originally published at prepsheets.com

React Select with options outside

In prepsheets.com users can specify which allergens ingredients and recipes have. The natural UI element for this is a multiselect. I was already using react-select which is an awesome library adding a stylable multi-select with lots of options (creating new options; async loading of options; etc). However after setting it up, it felt too cramped. If users had more than 3 allergen selected (some of which are quite long "") then the select would grow and become cumbersome. Instead I decided to place the selected options above the mutliselect. This article goes over how to do this.

Setting up

We're using react-select here so go ahead and install it

npm install react-select
Enter fullscreen mode Exit fullscreen mode

Now let's see what the default multi-select looks like

import Select from "react-select";

const options = [
  { value: "chocolate", label: "Chocolate" },
  { value: "strawberry", label: "Strawberry" },
  { value: "vanilla", label: "Vanilla" },
];

const MyComponent = () => <Select options={options} isMulti />;
Enter fullscreen mode Exit fullscreen mode

I'm using the 24 offical Irish allergens as you can see below when you add a certain amount the select grows which dosn't look nice in my opinion.

Mutliselect with many allergens selected

Moving selected options to outside the select

The next step is to not show the selected values in the select but rather above it. Let's create a wrapper component for React-Select

// OptionsOutsideSelect.js
import { Select } from "react-select";

const OptionsOutsideSelect = (props) => {
  const { isMulti, value } = props;

  return (
    <div>
      {isMulti ? value.map((val) => <span>{val.label} </span>) : null}
      <Select {...props} controlShouldRenderValue={!isMulti} />
    </div>
  );
};

export default OptionsOutsideSelect;
Enter fullscreen mode Exit fullscreen mode

If isMulti is true then we use the controlShouldRenderValue prop to hide the selected values and loop over the values prop and map them to <span> elements.

React Select with options outside

It should be possible to use the MultiValue component from react-select however I couldn't get that to work, which brings us to the next step

Styling the selected options

Let's change how we're mapping the selected values to look like

<ValuesContainer>
  {isMulti
    ? value.map((val) => <Value key={val.value}>{val.label}</Value>)
    : null}
</ValuesContainer>
Enter fullscreen mode Exit fullscreen mode

I'm going to use styled-components to style ValusContainer and Value but you can of course move the same rules over to whatever you're using

import styled from "styled-components";

const ValuesContainer = styled.div`
  display: flex;
  flex-wrap: wrap;
  align-items: center;
`;

const Value = styled.div`
  padding: 0.3rem 0.5rem 0.3rem 0.5rem;
  margin: 0 0.55rem 0.55rem 0;
  font-size: 0.75rem;
  color: black;
  background-color: rgba(247, 173, 46, 0.6);
  user-select: none;
`;
Enter fullscreen mode Exit fullscreen mode

This creates the below image which looks resonable

React Select styled option without X

The only thing left to do now is let users remove selected options

Adding a remove button

First we'll add an "X" button to all Values

<Value>
  <XButton name={val.value} onClick={handleRemoveValue}>
     // This is the mulitply ✕ not the x on your keyboard
  </XButton>
</Value>
Enter fullscreen mode Exit fullscreen mode

Now we need to make the handleRemoveValue function

const { onChange } = props;

const handleRemoveValue = (e) => {
  if (!onChange) return;
  const { name: buttonName } = e.currentTarget;
  const removedValue = value.find((val) => val.value === buttonName);
  if (!removedValue) return;
  onChange(
    value.filter((val) => val.value !== buttonName),
    { name, action: "remove-value", removedValue }
  );
};
Enter fullscreen mode Exit fullscreen mode

And Ta-Da 🎉 you have a react-select with the options rendered outside the select. I've included a code-sandbox below to show what this looks like

Top comments (0)