Introduction
What We Will Be Building
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:
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)
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?
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']`
Thanks for the post!
Is this you btw?
tailwindcomponents.com/component/m...