javascript 56 lines · 8 steps

Building a tag input in React

A controlled React component that turns typed text into deduplicated tag chips with keyboard editing.

Explained by highlit
1import { useState, useRef } from "react";
2 
3export function TagInput({ initialTags = [], onChange }) {
4 const [tags, setTags] = useState(initialTags);
5 const [draft, setDraft] = useState("");
6 const inputRef = useRef(null);
7 
8 const commit = (value) => {
9 const tag = value.trim().toLowerCase();
10 if (!tag || tags.includes(tag)) return;
11 const next = [...tags, tag];
12 setTags(next);
13 onChange?.(next);
14 };
15 
16 const removeAt = (index) => {
17 const next = tags.filter((_, i) => i !== index);
18 setTags(next);
19 onChange?.(next);
20 };
21 
22 const handleKeyDown = (e) => {
23 if (e.key === "Enter" || e.key === ",") {
24 e.preventDefault();
25 commit(draft);
26 setDraft("");
27 } else if (e.key === "Backspace" && draft === "" && tags.length) {
28 e.preventDefault();
29 removeAt(tags.length - 1);
30 }
31 };
32 
33 return (
34 <div className="tag-input" onClick={() => inputRef.current?.focus()}>
35 {tags.map((tag, i) => (
36 <span key={tag} className="chip">
37 {tag}
38 <button type="button" aria-label={`Remove ${tag}`} onClick={() => removeAt(i)}>
39 &times;
40 </button>
41 </span>
42 ))}
43 <input
44 ref={inputRef}
45 value={draft}
46 placeholder={tags.length ? "" : "Add a tag"}
47 onChange={(e) => setDraft(e.target.value)}
48 onKeyDown={handleKeyDown}
49 onBlur={() => {
50 commit(draft);
51 setDraft("");
52 }}
53 />
54 </div>
55 );
56}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Keeping the committed tags and the in-progress draft as separate state cleanly divides finished data from work in progress.
  2. 2Building a fresh array with the spread operator lets you update state and notify a parent from the same immutable value.
  3. 3Mapping keyboard events like Enter, comma, and Backspace onto commit and remove actions makes an input feel like a native tag editor.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a tag input in React — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code