DEV Community

Son Goku
Son Goku

Posted on

Build Multiselect component in few lines

Introduction

What We Will Be Building

React Multiselect

In this example, i will show you how you can build sime React Multiselect component. You can see basic logic behind component, and how you can use to build something simmilar yourself.

What is multiselect

React Multiselect - is a component that allows users to tick multiple options. Its implementation is quite simple, and in exchange brings a lot of UX value.

Prerequisites

  • This tutorial assumes that you have working knowledge of React
  • We are going to Use React Hooks
  • For css library I used here TailwindCSS, but you can use other if you like
  • Before we get started, ensure that you have Node, Yarn, or npm installed in your environment.
  • Have a Web browser ofcourse

Creating the React App

Now that we have all the information we will need to connect our application and we know what are we building, let's start building out our application!

Open your teminal, once there, we are going to be using the create-react-app command to get our application running. Run the following command:

npx create-react-app multiselect

Now to explain a bit.

The npx command will install and run the command create-react-app for us. The last part of that command is the name of our app. We are going to be naming our app multiselect, feel free to name it whatever you want.

Once cli has finished creating the project folder you can cd into it, and run it:

cd multiselect
npm start

Open your brower in type into url -> localhost:3000 to see our default React start page.

It will look like this:

React Starter

Install TailwindCSS

Next step is to install and configure tailwindcss. So I do not want to make this article too long, so you can check out these 2 guides on how to setup React with Tailwind:

React & Tailwind Setup
Configure React with Tailwind

Anyway If you do not want to use Tailwindcss, you can choose any other css framework (Bootstrap, Foundation etc), the code will work just fine.

Our Components

We will need 2 files, for this one to hold Multiselect component & one to hold Dropdown.

Within the /src folder, create a folder named /components. With that folder, create three files:

  • ./Multiselect.js
  • ./Dropdown.js

Multiselect Component

Now we will start working on MultiSelect component.
Open you Multiselect.js & paste following code inside it:

import React, { useState } from "react";

const MultiSelect = () => {
  const [items, setItems] = useState(["john", "milos", "steph", "kathreine"]);
  const [selectedItems, setSelected] = useState([]);

  return (
    <div className="autcomplete-wrapper">
      <div className="autcomplete">
        <div className="w-full flex flex-col items-center mx-auto">
          <div className="w-full">
            <div className="flex flex-col items-center relative">
              <div className="w-full ">
                <div className="my-2 p-1 flex border border-gray-200 bg-white rounded ">
                  <div className="flex flex-auto flex-wrap">
                    {selectedItems.map((tag, index) => {
                      return (
                        <div
                          key={index}
                          className="flex justify-center items-center m-1 font-medium py-1 px-2 bg-white rounded-full text-teal-700 bg-teal-100 border border-teal-300 "
                        >
                          <div className="text-xs font-normal leading-none max-w-full flex-initial">
                            {tag}
                          </div>
                          <div className="flex flex-auto flex-row-reverse">
                            <div>

                            </div>
                          </div>
                        </div>
                      );
                    })}
                    <div className="flex-1">
                      <input
                        placeholder=""
                        className="bg-transparent p-1 px-2 appearance-none outline-none h-full w-full text-gray-800"
                      />
                    </div>
                  </div>
                  <div className="text-gray-300 w-8 py-1 pl-2 pr-1 border-l flex items-center border-gray-200">
                    <button className="cursor-pointer w-6 h-6 text-gray-600 outline-none focus:outline-none">

                    </button>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default MultiSelect;

Adding Dropdown

Let's build out our dropdown now. We need our dropdown to open options and allow users to add/remove tags.

Now will introduce new Component Dropdown.js, do not forget to create it if you didn't already, and there put view of our dropdown

Also we will Updated Multiselect component with showing/hidding dropdown on click.

Here are our now our two updated components:

Dropdown.js

import React from "react";  

const Dropdown = ({ list }) => {  
  return (  
    <div  
      id="dropdown"  
      className="absolute shadow top-100 bg-white z-40 w-full lef-0 rounded max-h-select overflow-y-auto "  
    >  
      <div className="flex flex-col w-full">  
        {list.map((item, key) => {  
          return (  
            <div  
              key={key}  
              className="cursor-pointer w-full border-gray-100 rounded-t border-b hover:bg-teal-100"  
            >  
              <div className="flex w-full items-center p-2 pl-2 border-transparent border-l-2 relative hover:border-teal-100">  
                <div className="w-full items-center flex">  
                  <div className="mx-2 leading-6  ">{item}</div>  
                </div>  
              </div>  
            </div>  
          );  
        })}  
      </div>  
    </div>  
  );  
};  

export default Dropdown;  

Multiselect.js

import React, { useState } from 'react';
// import dropdown component
import Dropdown from './Dropdown';


const Multiselect = () => {
    // state showing if dropdown is open or closed
    const [dropdown, setDropdown] = useState(false);
    // managing dropdown items (list of dropdown items)
    const [items, setItems] = useState(['john', 'milos', 'steph', 'kathreine']);
    // contains multiselect items
    const [selectedItems, setSelected] = useState([]);

    // toggle dropdown open/close  
    const toogleDropdown = () => {
        setDropdown(!dropdown)
    };

    return (<div className="autcomplete-wrapper">
        <div className="autcomplete">
        <div className="w-full flex flex-col items-center mx-auto">
    <div className="w-full">
        <div className="flex flex-col items-center relative">
            <div className="w-full ">
                <div className="my-2 p-1 flex border border-gray-200 bg-white rounded ">
                    <div className="flex flex-auto flex-wrap">
                        {
                            selectedItems.map((tag, index) => {
                                return (
                                    <div key={index} className="flex justify-center items-center m-1 font-medium py-1 px-2 bg-white rounded-full text-teal-700 bg-teal-100 border border-teal-300 ">
                                            <div className="text-xs font-normal leading-none max-w-full flex-initial">{ tag }</div>
                                            <div className="flex flex-auto flex-row-reverse">
                                                <div>
                                                    <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" 
                                                    className="feather feather-x cursor-pointer hover:text-teal-400 rounded-full w-4 h-4 ml-2">
                                                        <line x1="18" y1="6" x2="6" y2="18"></line>
                                                        <line x1="6" y1="6" x2="18" y2="18"></line>
                                                    </svg>
                                                </div>
                                            </div>
                                        </div>)
                            })
                        }
                        <div className="flex-1">
                            <input placeholder="" className="bg-transparent p-1 px-2 appearance-none outline-none h-full w-full text-gray-800"/>
                        </div>
                    </div>
                    <div className="text-gray-300 w-8 py-1 pl-2 pr-1 border-l flex items-center border-gray-200" onClick={toogleDropdown}>
                        <button className="cursor-pointer w-6 h-6 text-gray-600 outline-none focus:outline-none">
                            <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-chevron-up w-4 h-4">
                                <polyline points="18 15 12 9 6 15"></polyline>
                            </svg>
                        </button>
                    </div>
                </div>
            </div>
        </div>
         { dropdown  ? <Dropdown list={items}></Dropdown>: null }
    </div>
</div>

    </div>
        </div>)
};

export default Multiselect;

Notice what we changed We've added dropdown component, look at the bottom of Multiselect component and added state for dropdown and toogleDropdown method to to switch between open and close state using: setDropdown(!dropdown).

Adding Option to Add/Remove Tags

Now this is the last step, we can add 2 methods to add & remove tags to and from multiselect.

This will give us options to add item to multiselect by clicking on it from dropdown list, and remove it from multiselect by click on X.

Check Updated Component:

Multiselect.js

import React, { useState } from 'react';
import Dropdown from './Dropdown';


const Multiselect = () => {
    // state showing if dropdown is open or closed
    const [dropdown, setDropdown] = useState(false);
    // managing dropdown items (list of dropdown items)
    const [items, setItems] = useState(['john', 'milos', 'steph', 'kathreine']);
    // contains selected items
    const [selectedItems, setSelected] = useState([]);


    const toogleDropdown = () => {
        setDropdown(!dropdown)
    };
    // adds new item to multiselect 
    const addTag = (item) => {
        setSelected(selectedItems.concat(item));
        setDropdown(false);
    };
    // removes item from multiselect
    const removeTag = (item) => {
        const filtered = selectedItems.filter((e) => e !== item);
        setSelected(filtered);
    }

    return (<div className="autcomplete-wrapper">
        <div className="autcomplete">
        <div className="w-full flex flex-col items-center mx-auto">
    <div className="w-full">
        <div className="flex flex-col items-center relative">
            <div className="w-full ">
                <div className="my-2 p-1 flex border border-gray-200 bg-white rounded ">
                    <div className="flex flex-auto flex-wrap">
                        {
                            selectedItems.map((tag, index) => {
                                return (
                                    <div key={index} className="flex justify-center items-center m-1 font-medium py-1 px-2 bg-white rounded-full text-teal-700 bg-teal-100 border border-teal-300 ">
                                            <div className="text-xs font-normal leading-none max-w-full flex-initial">{ tag }</div>
                                            <div className="flex flex-auto flex-row-reverse">
                                                <div onClick={() => removeTag(tag)}>
                                                    <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" 
                                                    className="feather feather-x cursor-pointer hover:text-teal-400 rounded-full w-4 h-4 ml-2">
                                                        <line x1="18" y1="6" x2="6" y2="18"></line>
                                                        <line x1="6" y1="6" x2="18" y2="18"></line>
                                                    </svg>
                                                </div>
                                            </div>
                                        </div>)
                            })
                        }
                        <div className="flex-1">
                            <input placeholder="" className="bg-transparent p-1 px-2 appearance-none outline-none h-full w-full text-gray-800"/>
                        </div>
                    </div>
                    <div className="text-gray-300 w-8 py-1 pl-2 pr-1 border-l flex items-center border-gray-200" onClick={toogleDropdown}>
                        <button className="cursor-pointer w-6 h-6 text-gray-600 outline-none focus:outline-none">
                            <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-chevron-up w-4 h-4">
                                <polyline points="18 15 12 9 6 15"></polyline>
                            </svg>
                        </button>
                    </div>
                </div>
            </div>
        </div>
         { dropdown  ? <Dropdown list={items} addItem={addTag}></Dropdown>: null }
    </div>
</div>

    </div>
        </div>)
};

export default Multiselect;

Dropdown.js

import React from 'react';


const Dropdown = ({list, addItem}) => {


    return (<div id="dropdown" className="absolute shadow top-100 bg-white z-40 w-full lef-0 rounded max-h-select overflow-y-auto ">
    <div className="flex flex-col w-full">
        { list.map((item, key) => {
            return <div key={key} 
            className="cursor-pointer w-full border-gray-100 rounded-t border-b hover:bg-teal-100" 
            onClick={() => addItem(item)}>
            <div className="flex w-full items-center p-2 pl-2 border-transparent border-l-2 relative hover:border-teal-100" >
                <div className="w-full items-center flex">
        <   div className="mx-2 leading-6  ">
            { item }
           </div>
          </div>
         </div>
        </div>
        })}
    </div>
</div>);

};

export default Dropdown;

What have we changed, nothing much, we added 2 new methods: addTag, removeTag in Multiselect, they will allow use to like their names say remove them and add them to multiselect

Conclusion

While this is a very simple tutorial, it shows how far we can get with just few lines of react code (most of the code is css classes from tailwind).

So what does this all mean? What have you learned? You learned how to create a React multiselect component with all the basic functionality encapsulated within it. You also learned how to pass values to the component, and how to use useState hook.

Now don't just stare there go and try buiding something yourself, because that is the fastest way you will learn!

If you have any questions, please don’t hesitate to leave a comment below.

If you liked this post, you can find more on:

Following me on Twitter:

Top comments (3)

Collapse
 
sampriti026 profile image
Sampriti

Hey there is a bug in this code, it allows the user to select the same tag multiple times. Can you look into it and lemme know how to resolve the same?

Collapse
 
timurtek_37 profile image
Timurtek

You can do something like this
`const list = ['car', 'house', 'dragon', 'superman'];
const str = 'superman';

if (list.indexOf(str) === -1) {
list.push(str);
}

console.log(list); ['car', 'house', 'dragon', 'superman']`

Collapse
 
cordazar profile image
Ricard Fredin

Thanks for the post!

Is this you btw?
tailwindcomponents.com/component/m...