Hello Reader, It's been a while I haven't written any tutorial, and last night I was working on a project and needed a TagInput component. Well, implementation is not that hard but it took me some time to come up with an idea to create it.
Before we start
You should be familiar with ReactJS and have a little knowledge of Material UI. Also, you should be familiar with what is Tag Input. If not below is an example of that.
Now, this is something we will be creating in this tutorial.
Implementation breakdown
If you haven't already figured out the implementation, Let me break it down for you. What we are doing is having a tag component and input component side by side. So, when a tag is added is just decreases the input area.
Below is a visual breakdown of the component.
I hope, you already understood what we are gonna implement in this tutorial. As we gonna use Material UI implementation going to be really easy.
Code Implementation
Once you have setup Reactjs and Material Ui create a new component say InputTags
and add material UI's `
export default function InputTags() {
return (
<Box sx={{ flexGrow: 1 }}>
<TextField
fullWidth
variant='standard'
size='small'
sx={{ margin: "1rem 0" }}
margin='none'
placeholder="Enter tags here"
/>
</Box>
);
}
Now let's create our tag component and style it as we want.
const Tags = () => {
return (
<Box
sx={{
background: "#283240",
height: "100%",
display: "flex",
padding: "0.4rem",
margin: "0 0.5rem 0 0",
justifyContent: "center",
alignContent: "center",
color: "#ffffff",
}}
>
<Stack direction='row' gap={1}>
<Typography>Tags</Typography>
<Cancel/>
/>
</Stack>
</Box>
);
};
we are done with the basics, we need a way to show the tags inside of the input component. Fortunately, we have a prop called InputProps
in Material UI.
You can read all about this here.
once we add the inside the InputProps out component will look like the below. Also
const Tags = () => {
return (
<Box
sx={{
background: "#283240",
height: "100%",
display: "flex",
padding: "0.4rem",
margin: "0 0.5rem 0 0",
justifyContent: "center",
alignContent: "center",
color: "#ffffff",
}}
>
<Stack direction='row' gap={1}>
<Typography>Tags</Typography>
<Cancel/>
/>
</Stack>
</Box>
);
};
export default function InputTags (){
return (
<Box sx={{ flexGrow: 1 }}>
<TextField
fullWidth
variant='standard'
size='small'
sx={{ margin: "1rem 0" }}
margin='none'
placeholder="Enter Tags here"
InputProps={{
startAdornment: (
<Box sx={{ margin: "0 0.2rem 0 0", display: "flex" }}>##
<Tags />
);
})}
</Box>
),
}}
/>
</Box>
)
}
once you render the above component you will see something like below on the screen.
Now we are done with the UI and design. Let's add logic to it.
Adding logic to Input tag component.
Well, let's discuss how we gonna implement the logic here.
Algorithm
Here what we can do is create a Tag array where we keep all the tags and then by using javascript's
.map()
method we can display all the tags inside theInputProps
Also for adding new tags, we just create an
onSubmit()
method so that every time a user clicks on the entering button the tag will be added to theTags[]
array.And for deleting we will create
handleDelete()
method and pass the tag name as a parameter so that when we run.filter()
method on theTags[]
we can remove the tags we passed as a parameter.
Code Implementation of the above Algorithm.
- Creating a
Tag[]
as a state. Also mapping the tags such that it will show in that 'InputProps' of the<Textfield/>
component.
const [tags, SetTags] = useState([]);
also our <TextField/>
will look something like below
<TextField
fullWidth
variant='standard'
size='small'
sx={{ margin: "1rem 0" }}
margin='none'
placeholder={tags.length < 5 ? "Enter tags" : ""}
InputProps={{
startAdornment: (
<Box sx={{ margin: "0 0.2rem 0 0", display: "flex" }}>
{tags.map((data, index) => {
return (
<Tags data={data} key={index} />
);
})}
</Box>
),
}}
/>
also, we change the <Tags/>
component as follows.
const Tags = ({ data}) => {
return (
<Box
sx={{
background: "#283240",
height: "100%",
display: "flex",
padding: "0.4rem",
margin: "0 0.5rem 0 0",
justifyContent: "center",
alignContent: "center",
color: "#ffffff",
}}
>
<Stack direction='row' gap={1}>
<Typography>{data}</Typography>
<Cancel
sx={{ cursor: "pointer" }}
/>
</Stack>
</Box>
);
};
- Adding
onSubmit()
method and also<form>
tags to the<Textfield/>
. Also to get the input value we will use React hooksuseRef()
.
const tagRef = useRef();
//HandleSubmit
const handleOnSubmit = (e) => {
e.preventDefault();
SetTags([...tags, tagRef.current.value]);
tagRef.current.value = "";
};
also, we will change our ` component as below.
<form onSubmit={handleOnSubmit}>
<TextField
inputRef={tagRef}
fullWidth
variant='standard'
size='small'
sx={{ margin: "1rem 0" }}
margin='none'
placeholder={tags.length < 5 ? "Enter tags" : ""}
InputProps={{
startAdornment: (
<Box sx={{ margin: "0 0.2rem 0 0", display: "flex" }}>
{tags.map((data, index) => {
return (
<Tags data={data} key={index} />
);
})}
</Box>
),
}}
/>
</form>
once done you will be able to add tags every time you click on the enter button.
here is a preview of what we have made so far.
- Adding a delete feature in our tags.
creating a handledelete()
method.
const handleDelete = (value) => {
const newtags = tags.filter((val) => val !== value);
SetTags(newtags);
};
passing handleDelete()
as a prop to
<TextField
inputRef={tagRef}
fullWidth
variant='standard'
size='small'
sx={{ margin: "1rem 0" }}
margin='none'
placeholder={tags.length < 5 ? "Enter tags" : ""}
InputProps={{
startAdornment: (
<Box sx={{ margin: "0 0.2rem 0 0", display: "flex" }}>
{tags.map((data, index) => {
return (
<Tags data={data} handleDelete={handleDelete} key={index} />
);
})}
</Box>
),
}}
/>
In <Tags/>
component we map the handledelete()
to our <Cancel/>
icon and pass {data}
as a parameter.
const Tags = ({ data, handleDelete }) => {
return (
<Box
sx={{
background: "#283240",
height: "100%",
display: "flex",
padding: "0.4rem",
margin: "0 0.5rem 0 0",
justifyContent: "center",
alignContent: "center",
color: "#ffffff",
}}
>
<Stack direction='row' gap={1}>
<Typography>{data}</Typography>
<Cancel
sx={{ cursor: "pointer" }}
onClick={() => {
handleDelete(data);
}}
/>
</Stack>
</Box>
);
};
We are done, our component is ready so let's see the working of it.
Our final product.
Final Code
import { Cancel, Tag } from "@mui/icons-material";
import { FormControl, Stack, TextField, Typography } from "@mui/material";
import { Box } from "@mui/system";
import { useRef, useState } from "react";
const Tags = ({ data, handleDelete }) => {
return (
<Box
sx={{
background: "#283240",
height: "100%",
display: "flex",
padding: "0.4rem",
margin: "0 0.5rem 0 0",
justifyContent: "center",
alignContent: "center",
color: "#ffffff",
}}
>
<Stack direction='row' gap={1}>
<Typography>{data}</Typography>
<Cancel
sx={{ cursor: "pointer" }}
onClick={() => {
handleDelete(data);
}}
/>
</Stack>
</Box>
);
};
export default function InputTags() {
const [tags, SetTags] = useState([]);
const tagRef = useRef();
const handleDelete = (value) => {
const newtags = tags.filter((val) => val !== value);
SetTags(newtags);
};
const handleOnSubmit = (e) => {
e.preventDefault();
SetTags([...tags, tagRef.current.value]);
tagRef.current.value = "";
};
return (
<Box sx={{ flexGrow: 1 }}>
<form onSubmit={handleOnSubmit}>
<TextField
inputRef={tagRef}
fullWidth
variant='standard'
size='small'
sx={{ margin: "1rem 0" }}
margin='none'
placeholder={tags.length < 5 ? "Enter tags" : ""}
InputProps={{
startAdornment: (
<Box sx={{ margin: "0 0.2rem 0 0", display: "flex" }}>
{tags.map((data, index) => {
return (
<Tags data={data} handleDelete={handleDelete} key={index} />
);
})}
</Box>
),
}}
/>
</form>
</Box>
);
}
What we learned
- How to use Material UI's `
- How to create a new custom component.
- How to add and delete data from the state.
- How to use useRef hook.
- How to create a custom Input Tag component
If you like the tutorial like the blog also give me a follow on Twitter