This article is for those who want to learn how to create tags, that later on, can be deleted, rearranged by drag and drop and also it prevents the user to create duplicates.

Let’s start with the code:
import React, { useState } from 'react';
import styled from 'styled-components';
const TagCreatorContainer = styled.div`
display: flex;
flex-wrap: wrap;
align-items: center;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px;
`;
const TagPill = styled.div`
display: inline-flex;
align-items: center;
background-color: #f2f2f2;
color: #333;
font-size: 14px;
border-radius: 16px;
padding: 2px 8px;
margin: 4px;
cursor: grab;
user-select: none;
&:hover {
opacity: 0.8;
}
&:active {
cursor: grabbing;
}
`;
const TagCloseButton = styled.button`
padding: 0;
line-height: 12px;
background-color: transparent;
border: none;
color: #888;
font-size: 22px;
cursor: pointer;
margin-left: 4px;
color: red;
`;
const TagInput = styled.input`
border: none;
outline: none;
padding: 8px;
font-size: 14px;
flex-grow: 1;
`;
const TagCreator = () => {
const [tags, setTags] = useState<string[]>([]);
const [newTag, setNewTag] = useState('');
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setNewTag(event.target.value);
};
const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === ',' || event.key === 'Enter') {
event.preventDefault();
createTag();
} else if (event.key === 'Backspace' && newTag === '') {
removeTag(tags.length - 1);
}
};
const createTag = () => {
if (newTag.trim() === '') return;
const updatedTags = new Set([...tags, newTag.trim()]);
setTags(Array.from(updatedTags));
setNewTag('');
};
const removeTag = (index: number) => {
const updatedTags = [...tags];
updatedTags.splice(index, 1);
setTags(updatedTags);
};
const handleTagDragStart = (event: React.DragEvent<HTMLDivElement>, index: number) => {
event.dataTransfer.setData('text/plain', index.toString());
};
const handleTagDragOver = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
};
const handleTagDrop = (event: React.DragEvent<HTMLDivElement>, dropIndex: number) => {
const draggedIndex = parseInt(event.dataTransfer.getData('text/plain'), 10);
const updatedTags = [...tags];
const [draggedTag] = updatedTags.splice(draggedIndex, 1);
updatedTags.splice(dropIndex, 0, draggedTag);
setTags(updatedTags);
};
return (
<div>
<TagCreatorContainer>
{tags.map((tag, index) => (
<TagPill
key={index}
draggable
onDragStart={(event) => handleTagDragStart(event, index)}
onDragOver={handleTagDragOver}
onDrop={(event) => handleTagDrop(event, index)}
>
{tag}
<TagCloseButton onClick={() => removeTag(index)}>×</TagCloseButton>
</TagPill>
))}
<TagInput
type="text"
value={newTag}
onChange={handleInputChange}
onKeyDown={handleInputKeyDown}
placeholder="Type a tag and press Enter"
/>
</TagCreatorContainer>
</div>
);
};
export default TagCreator;
Let me explain

First of all we set up the tags state where we will store our already created tags and the newTag state that will hold the value of the input. We can also get the value of the input only when we hit enter or the comma key, so it’s not mandatory to keep in in a state.

We can create or remove tags when clicking on the red X button on each tag.
When we create a new tag first we check of the newTag state has text inside besides space, so we first trim it and then check if anything remained. If yes, we merge together the already existing tags with the newTag string, we have to make sure that the new tag does not already exist in the tags array, so we use the Set object for that.(The Set object automatically filters out duplicates).
Of course we need to convert the Set object back to an array, so we used the Array.from() method for that. Finally we reset the newTag state.
Removing a tag is not harder then creating. We need as argument, the index of the clicked tag. We spread out the tags in a new array, because we don’t mutate the original tags state, we work with copies, always. Then we delete 1 element starting from the index from the argument and finally we update the tags state.

So the question is when do we call the createTag function and when the remooveTag function? We set up the event handler for the keydown event and check if comma or Enter key was pressed then create the tag if backspace was hit then remove the last tag.
Drop it like it’s hot
The drag and drop functionality is not hard to be added to our component. We set up a onDragStart, onDragOver and an onDrop event to our Tags.

After setting up the events we create the callbacks for them.

The event.dataTransfer is used to hold the data that is being dragged during a drag and drop operation.
By default, when an element is being dragged over another element, the browser might not allow it to be dropped. Therefore, we need to prevent the default behaviour in the handleTagDragOver function to indicate that the drop is allowed.
The handleTagDrop function fetches the data stored in the DataTransfer object. First we remove the tag from it’s current position with the splice(draggedIndex, 1). After this we add our dragged tag to the dropIndex position. The onDrop event will occur on the tag over which we want to drop our dragged element(and not on the dragged element).
That is mostly it, enjoy and of course don’t forget to like, share, subscribe and comment.