webdevpuneet.com

Tag Input — Free HTML CSS JS Snippet

Tag / Chip Input · Forms · Plain HTML, CSS & JS · Live preview

CategoryForms

Copy & paste

Source Code

<div class="demo">
  <label class="field-label">Skills</label>
  <div class="tag-field" id="tag-field" onclick="document.getElementById('tag-inp').focus()">
    <div class="tags" id="tags">
      <span class="tag">React<button onclick="removeTag(this)">×</button></span>
      <span class="tag">TypeScript<button onclick="removeTag(this)">×</button></span>
      <span class="tag">CSS<button onclick="removeTag(this)">×</button></span>
    </div>
    <input id="tag-inp" class="tag-inp" placeholder="Add skill…" onkeydown="handleKey(event)" />
  </div>
  <p class="hint">Press Enter or comma to add a tag</p>

  <div class="suggestions">
    <span class="sug-label">Suggestions:</span>
    <button class="sug" onclick="addTag('JavaScript')">JavaScript</button>
    <button class="sug" onclick="addTag('Node.js')">Node.js</button>
    <button class="sug" onclick="addTag('Figma')">Figma</button>
    <button class="sug" onclick="addTag('Vue')">Vue</button>
    <button class="sug" onclick="addTag('GraphQL')">GraphQL</button>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Enter and comma keydown triggers addTag() and clears input
Duplicate check: querySelectorAll(".tag").map(textContent) prevents repeats
Each tag is a span with text and a × remove button
Remove button onclick calls this.parentNode.remove()
Backspace on empty input removes last tag via .at(-1)?.remove()
Preset tags in HTML are styled identically to dynamically added tags
Input expands within the tag container flex layout
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Tag Input — Enter to Add, Duplicate Check & Backspace Remove

Screenshot of the Tag / Chip Input snippet rendered live

A tag input lets users type and add multiple tags or labels — used in filter selectors, skill lists, email address fields, and keyword inputs (the multi-select dropdown is the picker-based cousin). This snippet implements the full tag interaction: Enter to add, comma to add, duplicate prevention, click-to-remove, and backspace-to-undo.

Adding tags

The input listens for keydown. On Enter or , (comma), addTag(input.value) is called and the input is cleared. addTag(v) trims whitespace, checks for duplicates via querySelectorAll('.tag').map(...), and returns early if the tag already exists or is empty.

The tag element

Each tag is a <span class="tag"> with the tag text and a <span class="remove">×</span> button. The remove button's onclick calls tag.remove() — removing just that span from the DOM.

Backspace to remove last tag

When the input is empty and the user presses Backspace, the last tag is removed: document.querySelectorAll('.tag').at(-1)?.remove(). This matches the behaviour of email address inputs in Gmail and similar applications.

The Enter/comma add pattern

The tag input listens for keydown events. When Enter or comma is pressed, it calls addTag() which trims the input value, checks for duplicates, adds the tag to the array, creates a chip element, and clears the input. Comma detection uses e.key === ',' with e.preventDefault() to stop the comma character appearing in the input after the tag is added.

The backspace-last-remove pattern

When the input is empty (input.value === '') and Backspace is pressed, the last tag in the array is removed and the corresponding chip element is deleted from the DOM. This matches the standard tag input interaction that users expect from email address fields, filter chips, and CRM contact selectors.

Duplicate prevention

Before adding a tag, the current tags array is checked: if (tags.includes(newTag)) return. Show a brief shake animation on the existing duplicate chip to communicate "already added" without an error message. Add .shake { animation: shake 0.3s } to flash the duplicate chip briefly.

Connecting to form submission

Collect tag values for form submission: document.querySelector('form').addEventListener('submit', e => { e.preventDefault(); const values = tags; submitForm(values); }). Or use a hidden input to hold a JSON-encoded array: hiddenInput.value = JSON.stringify(tags). This makes the tag values available to standard HTML form submission without JavaScript preprocessing.

The Enter/comma add pattern

The tag input listens for keydown events. When Enter or comma is pressed, it calls addTag() which trims the input value, checks for duplicates, adds the tag to the array, creates a chip element, and clears the input. Comma detection uses e.key === ',' with e.preventDefault() to stop the comma character appearing in the input after the tag is added.

The backspace-last-remove pattern

When the input is empty (input.value === '') and Backspace is pressed, the last tag in the array is removed and the corresponding chip element is deleted from the DOM. This matches the standard tag input interaction that users expect from email address fields, filter chips, and CRM contact selectors.

Duplicate prevention

Before adding a tag, the current tags array is checked: if (tags.includes(newTag)) return. Show a brief shake animation on the existing duplicate chip to communicate "already added" without an error message. Add .shake { animation: shake 0.3s } to flash the duplicate chip briefly.

Connecting to form submission

Collect tag values for form submission: document.querySelector('form').addEventListener('submit', e => { e.preventDefault(); const values = tags; submitForm(values); }). Or use a hidden input to hold a JSON-encoded array: hiddenInput.value = JSON.stringify(tags). This makes the tag values available to standard HTML form submission without JavaScript preprocessing.

Step by step

How to Use

  1. 1
    Type and press EnterType a tag in the input and press Enter or comma to add it. Duplicate tags are rejected silently.
  2. 2
    Remove a tagClick the × on any tag to remove it from the list.
  3. 3
    Backspace to remove last tagWhen the input is empty, press Backspace to remove the most recently added tag.
  4. 4
    Update preset tagsIn the HTML panel, change the preset .tag spans to your default tags. Each needs a .remove span with onclick="this.parentNode.remove()".
  5. 5
    Limit tag countIn addTag(), add a check: if (existing.length >= 5) return; to limit the number of tags.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

Skills and interest selectors
Used on profile forms for tech stacks, skills, interests, and hobbies. Users type and add tags rather than selecting from a fixed dropdown.
Email recipient fields
The email tag input pattern used in Gmail and Outlook — type an address and press Enter to add it as a pill. Each address is removable.
Blog post and content tagging
Add a tag input to post creation forms. Authors type keywords that tag the post for search and categorisation.
Learn DOM manipulation for dynamic lists
The tag input creates and removes DOM elements dynamically. Edit addTag() and the remove onclick to understand element creation and removal patterns.
Search filter chip input
Use as a filter chip input above a search results grid. Each added tag filters the results; removing a tag restores removed items.
Read tag values for form submission
Collect tags: const tags = [...document.querySelectorAll(".tag")].map(t => t.textContent.replace("×","").trim()). Pass as array to your API.

Got questions?

Frequently Asked Questions

addTag() calls querySelectorAll(".tag") and maps each element to its textContent with the × stripped. If the new value is already in this array, the function returns early without adding the tag.

const tags = [...document.querySelectorAll(".tag")].map(t => t.textContent.replace("×","").trim()); gives an array of tag strings. Append as JSON to a hidden form input or include directly in a fetch request body.

In addTag(), before creating the span: const count = document.querySelectorAll(".tag").length; if (count >= maxTags) return; — where maxTags is your limit.

Call addTag(value) from the dropdown item click handler. The duplicate check and DOM creation work the same way regardless of the trigger.

Yes. Click "JSX" for a React component. In React, manage tags as an array in useState. Add/remove by spreading: setTags(prev => [...prev, newTag]) and setTags(prev => prev.filter(t => t !== tag)).

Add a data-category attribute when creating the span. In CSS, use [data-category="design"] { background: #e0f2fe } etc. to colour-code tag categories.