- ⚡ Autosuggest with fuzzy matching for smarter suggestions (type "many" to find "Germany")
- ♿ WCAG 2.1 AA Accessible - ARIA support, keyboard navigation, screen reader announcements
- ⌨️ Advanced keyboard navigation - Arrow keys, Home/End, Tab, Escape fully supported
- ✏️ Edit the tags inline by double clicking them.
- 🏷️ Paste strings with delimiters of your choice and the component will create the tags for you.
- 🗑️ Quickly delete the tags with a visual confirmation before removing a tag.
- 🧹 Quickly clear all tags with
quick-deletemode. - 🔒 Lock the component using the
readonlymode. - ✋ Restrict the number of tags and Handle duplicates gracefully.
- 🌈 Customize the colors.
- 📱 Mobile optimized - 44px touch targets, responsive layout, virtual keyboard support
- 🎯 Intelligent error handling - Real-time error messages, empty states, result counts
- ⚡ Installation
- 🚀 Getting Started
- 🎯 Nuxt 3 Support
- 🍬 Demos
- Keyboard Shortcuts
- Accessibility
- Props
- Migration Guide (v0.5.0+)
- 📦 Build Setup
- 🔨 Contributing
- Notes
- Meta
# Using npm
npm install smart-tagz
# Using yarn
yarn add smart-tagz
# Using pnpm
pnpm add smart-tagzsmart-tagz has some great defaults to get you started quickly. Please check the props list for all options.
<template>
<smart-tagz
autosuggest
editable
inputPlaceholder="Select Countries ..."
:sources="sources"
:allowPaste="{delimiter: ','}"
:allowDuplicates="false"
:maxTags="20"
:defaultTags="['United Kingdom', 'Uruguay', 'Uzbekistan']"
/>
</template>
<script>
import { SmartTagz } from "smart-tagz";
import "smart-tagz/dist/smart-tagz.css";
import { defineComponent } from "vue";
export default defineComponent({
name: "Basic",
components: {
SmartTagz,
}
});
</script>Smart-Tagz works seamlessly with Nuxt 3! We provide an official Nuxt 3 module for easy integration.
npm install smart-tagzAdd to nuxt.config.ts:
export default defineNuxtConfig({
modules: [
'smart-tagz/nuxt'
]
})Use it in your components:
<template>
<SmartTagz
:sources="languages"
:on-changed="handleChanged"
/>
</template>
<script setup>
const languages = ref(['JavaScript', 'TypeScript', 'Python'])
const handleChanged = (tags) => {
console.log('Tags:', tags)
}
</script>Create a plugin file plugins/smart-tagz.ts:
import { defineNuxtPlugin } from '#app'
import SmartTagz from 'smart-tagz'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.component('SmartTagz', SmartTagz)
})- ✅ Full SSR/SSG support
- ✅ Auto-imported components (via Nuxt module)
- ✅ TypeScript support
- ✅ Reactive source updates
- ✅ Programmatic control via refs
- ✅ All Vue 3 Composition API features supported
Programmatic Tag Control:
<template>
<div>
<SmartTagz ref="tagsRef" editable />
<button @click="addTag">Add Tag</button>
</div>
</template>
<script setup>
const tagsRef = ref()
const addTag = () => tagsRef.value?.handleAddTag('new-tag')
</script>Async Data Loading:
<script setup>
const tags = ref([])
onMounted(async () => {
const response = await $fetch('/api/tags')
tags.value = response
})
</script>For complete Nuxt 3 documentation, see the Nuxt 3 Guide or check the docs.
Head to our demo page for examples showcasing all the features.
https://smart-tagz.vercel.app/
Smart-Tagz supports full keyboard navigation for accessibility and efficiency:
| Key | Action |
|---|---|
| ↑ / ↓ | Navigate suggestions up/down |
| Home | Jump to first suggestion |
| End | Jump to last suggestion |
| Enter | Select highlighted suggestion or add typed text |
| Tab | Select highlighted suggestion and move focus |
| Escape | Close suggestions and clear selection |
| Backspace | Delete character or last tag when input is empty |
| Delete | Delete last tag (press again to confirm) |
| Ctrl + A | Select all tags for quick deletion (when quickDelete enabled) |
Smart-Tagz is built with WCAG 2.1 Level AA compliance in mind:
- ARIA Support: Proper roles (
combobox,listbox,option) and attributes (aria-expanded,aria-selected,aria-label) - Screen Reader Announcements:
- Tag additions: "France added. 3 of 20 tags"
- Tag removals: "Germany removed. 2 of 20 tags"
- Error states: "Maximum 20 tags allowed"
- Filtered results: Announced via live region
- Keyboard Navigation: All interactions possible without mouse
- Focus Management: Visible focus indicators on all interactive elements
- Semantic HTML: Proper use of
<ul>,<li>for suggestions - Color Independence: Error states use icons and text, not color alone
- Windows: NVDA (free), JAWS
- macOS: VoiceOver (built-in)
- iOS: VoiceOver (built-in)
- Android: TalkBack (built-in)
<smart-tagz
autosuggest
:sources="countries"
:maxTags="20"
:allowDuplicates="false"
:onChanged="handleChange"
/>
<!-- Screen reader output:
"Add tags, 0 of 20"
Input receives focus with aria-label and description
User types "fran", suggestions appear
"5 results" announcement
User selects "France"
"France added. 1 of 20 tags" announcement
-->| Prop | Type | Description | Default |
|---|---|---|---|
| defaultTags | Array | initialize with a default set of tags |
[] |
| width | String | width of the container |
100% |
| autosuggest | Boolean | Enables the autosuggest feature. you also need to set the sources for the autosuggest to work. |
false |
| sources | Array | Works as the datasource for the autosuggest feature |
[] |
| allowPaste | { delimiter: String } | Parses the pasted string based on the passed delimiter and creates tags automatically | {delimiter: ","} |
| editable | Boolean | makes the tags editable |
false |
| allowDuplicates | Boolean | allows/disallows duplicate tag entries while pasted or entered manually. |
true |
| maxTags | Number | sets the Maximum number of tags |
20 |
| inputPlaceholder | String | Placeholder for the input box. |
"Enter tag..." |
| readOnly | Boolean | Makes the whole component readOnly. ideal for display only purposes. |
false |
| quick-delete | Boolean | When enabled all the tags can be cleared by CTRL + A, DEL | false |
| on-changed | Function | callback that gets called when a new tag is added or an existing tag is deleted |
false |
Note: Props can be passed using either camelCase (
quickDelete) or kebab-case (:quick-delete) in Vue templates. The table uses kebab-case for consistency with HTML attribute conventions.
We can initialize smart-tagz with some default tags. This setting will mostly be used along with the readonly prop to create tags for display only purposes.
<smart-tagz :default-tags="['United Kingdom', 'Uruguay', 'Uzbekistan']" />You can decide how to manage duplicate tags by either allowing or disallowing them completely. When set to false no duplicate values are allowed.
<smart-tagz :allow-duplicates="false" />When set to true, the autosuggest prop suggests values in a dropdown using smart fuzzy matching. You also need to set the sources prop for this to work. The sources prop can be an Array of strings.
Smart-Tagz uses intelligent fuzzy matching powered by fuse.js to provide flexible searching:
- Smart matching: Type "many" to find "Germany", "germ" to find "Germany", etc.
- Typo tolerance: Slightly misspelled words still match
- Partial matching: Match any part of the word, not just the beginning
- Real-time results: Suggestions update as you type with result count
<smart-tagz
autosuggest
:sources="['India', 'Brazil', 'China', 'United Kingdom']"
:on-changed="handleTagsChange"
/>
<!-- Type "ind" → finds "India" -->
<!-- Type "bra" → finds "Brazil" -->
<!-- Type "uni" → finds "United Kingdom" -->- Result highlighting: Matching characters are highlighted in yellow
- Result count: Shows "3 results" or "1 result" above suggestions
- Empty state: Shows "No matches found for 'xyz'" when nothing matches
The component can also be configured to accept the Maximum number of tags that can be created. Once the threshold is reached, the input will be hidden from the user.
Here we restrict the tags to 3
<smart-tagz :max-tags="3" />The component can parse strings and automatically create tags for you. The default delimiter is "," but you can override this setting by manually setting the delimiter option.
<smart-tagz :allow-paste="{delimiter: ';'}" />The Tags are not editable by default, but you can change this setting with the editable prop. Simply double click a tag, make the changes and hit enter to save.
<smart-tagz editable />You can lock the component with readonly mode. All interactions are disabled in read-only mode.
<smart-tagz read-only />The components color scheme can be customized by passing a custom theme prop.
<smart-tagz
:theme="{
primary: '#545454',
background: '#bdbdbd',
tagTextColor: '#fff',
}"
/>Theme Properties:
primary(String, required): Main color for tag background and suggestion panetagTextColor(String, required): Text color for tagsbackground(String, optional): Container background colorsecondary(String, optional): Additional accent color (currently unused)
If you are looking for more control in terms of customizing the style of the tags, you can make use of the classNames prop to apply custom classes to the different elements within the component.
<smart-tagz
input-placeholder="Select Countries ..."
:class-names="{
wrapper: 'my-tags-wrapper',
tag_container: 'my-tag-container',
tag_name: 'my-tag-name',
tag_close_btn: 'my-close-btn',
}"
:default-tags="[
'United Kingdom',
'Uruguay',
'Uzbekistan',
'Venezuela'
]"
/>Before (v0.4.x): Prefix-only matching
Input: "many" → Suggestions: (no results)
After (v0.5.0+): Smart fuzzy matching
Input: "many" → Suggestions: ["Germany", "Myanmar"]
Migration: Update your tests and documentation to reflect fuzzy matching behavior. No code changes required unless you relied on prefix-only behavior.
Added: fuse.js (~7KB gzipped) for fuzzy search
- Already included in package.json
- No configuration needed
- Improves search quality automatically
New utility class added:
.sr-only // Screen reader only text (hidden visually, visible to screen readers)Updated styles:
.suggest-pane-item: Font size reduced fromvar(--font-size-sm)to0.9rem.suggest-pane-item--selected: Added left border (3px) indicator.suggest-pane-item: Added 600 font-weight for better hierarchy
Before: ~16px After: 44px (iOS HIG minimum, better mobile accessibility)
May affect layouts that assumed smaller buttons. Adjust styles if needed:
.tag-container__button {
// Now: width: 44px; height: 44px;
// Override if needed:
@include size(24px); // your size here
}Added for accessibility. No breaking changes, but updated DOM structure:
<!-- Input now has -->
<input
role="combobox"
aria-expanded="true"
aria-autocomplete="list"
aria-controls="suggestions-listbox"
aria-activedescendant="suggestion-0"
aria-label="Add tags (0 of 20)"
/>
<!-- Suggestions now have -->
<ul role="listbox" aria-label="Tag suggestions" id="suggestions-listbox">
<li role="option" aria-selected="false" id="suggestion-0">...</li>
</ul>✅ Screen reader announcements for tag operations
✅ Live error messages (duplicate/max tags)
✅ Empty state messaging ("No matches found")
✅ Result count indicator
✅ Result highlighting in suggestions
✅ Enhanced keyboard navigation (Home, End, Tab)
✅ Mobile-optimized touch targets
✅ Responsive dropdown height (80vh on desktop, 60vh on mobile)
✅ Better focus indicators with :focus-visible
- Review fuzzy matching behavior in your use cases
- Test with keyboard navigation (arrows, Home, End, Tab)
- Test with screen readers (optional but recommended)
- Verify button sizes in your layout
- Update unit tests for new matching behavior
- Update documentation for end users
Smart-Tagz uses the following core dependencies:
| Package | Version | Purpose |
|---|---|---|
| fuse.js | ^7.1.0 | Fuzzy search and matching for autosuggest feature |
| vue-feather-icons | ^5.1.0 | Icon components (peer dependency) |
| escape-string-regexp | ^5.0.0 | Utility for escaping special characters in regex patterns |
| shiki | ^3.15.0 | Syntax highlighting for code blocks in demo (demo-only) |
Note:
vue-feather-iconsis a peer dependency, so it must be installed separately in your project.
# install dependencies (using pnpm)
pnpm install
# start dev
pnpm dev
# build library
pnpm build
# run css linting
pnpm lint:css
# run css linting and fix
pnpm lint:css:fix
# run all tests
pnpm test
# run tests with coverage
pnpm test:coverageNote: The project uses pnpm for dependency management. You can also use npm or yarn, but pnpm is recommended.
- Fork it ( https://github.com/prabhuignoto/smart-tagz/fork )
- Create your feature branch (
git checkout -b new-feature) - Commit your changes (
git commit -am 'Add feature') - Push to the branch (
git push origin new-feature) - Create a new Pull Request
The project uses vite instead of @vue/cli. I choose vite for speed and i also believe vite will be the future.
Prabhu Murthy – @prabhumurthy2 – [email protected]
Distributed under the MIT license. See LICENSE for more information.

