Maximum Tag Limit
Learn how to set and enforce a maximum number of tags.
Live Demo
Try adding more than 3 tags (the input will be disabled):
Overview
The maxTags prop limits the number of tags users can add. This is useful for:
- Form field constraints
- API limitations
- UX simplification
- Data validation
- Preventing data overload
Basic Usage
vue
<!-- Limit to 5 tags -->
<template>
<SmartTagz :max-tags="5" />
</template>
<!-- Limit to 10 tags (default) -->
<template>
<SmartTagz :max-tags="10" />
</template>
<script setup>
import SmartTagz from 'smart-tagz'
import 'smart-tagz/dist/smart-tagz.css'
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Props
maxTags
- Type:
number - Default:
20 - Description: Maximum number of tags that can be added
vue
<SmartTagz :max-tags="5" />1
How It Works
Before Limit Reached
- Input field is visible
- Users can add tags normally
- ARIA label shows progress: "Add tags (2 of 5)"
At Limit
- Input field automatically hides
- No more tags can be added
- Error message shown if user tries: "Maximum 5 tags allowed"
- Input field reappears when tags are removed
Exceeding Limit with Paste
- Paste only adds tags up to the limit
- Excess tags are ignored
- Error message shown for overflow
Practical Examples
Example 1: Survey with Tag Limit
vue
<template>
<div class="survey">
<h2>What are your top skills?</h2>
<p>Select up to 5 skills</p>
<SmartTagz
:max-tags="5"
:sources="availableSkills"
autosuggest
input-placeholder="Select skills..."
@on-changed="handleSkillsChange"
/>
<div class="progress">
<span>{{ selectedSkills.length }} / 5 skills selected</span>
<div class="progress-bar">
<div class="progress-fill" :style="{ width: progressPercent }"></div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import SmartTagz from 'smart-tagz'
const availableSkills = [
'JavaScript', 'Python', 'Vue.js', 'React', 'TypeScript',
'Node.js', 'Docker', 'AWS', 'PostgreSQL', 'GraphQL'
]
const selectedSkills = ref([])
const progressPercent = computed(() => {
return (selectedSkills.value.length / 5) * 100 + '%'
})
const handleSkillsChange = (skills) => {
selectedSkills.value = skills
}
</script>
<style>
.survey {
max-width: 600px;
margin: 0 auto;
padding: 2rem;
}
.progress {
margin-top: 1.5rem;
}
.progress-bar {
width: 100%;
height: 8px;
background-color: #e5e7eb;
border-radius: 4px;
overflow: hidden;
margin-top: 0.5rem;
}
.progress-fill {
height: 100%;
background-color: #3b82f6;
transition: width 0.3s ease;
}
</style>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
Live Demo - Try selecting up to 5 skills:
What are your top skills?
Select up to 5 skills
Example 2: Shopping Cart Tags (Wishlist)
vue
<template>
<div class="wishlist">
<h2>My Wishlist</h2>
<SmartTagz
:max-tags="10"
:editable="true"
:allow-duplicates="false"
input-placeholder="Add item to wishlist..."
@on-changed="updateWishlist"
/>
<div v-if="wishlistItems.length >= 10" class="full-notice">
<p>⚠️ Your wishlist is full. Remove items to add more.</p>
</div>
<div class="wishlist-actions">
<button @click="shareWishlist" :disabled="wishlistItems.length === 0">
Share Wishlist
</button>
<button @click="clearWishlist">Clear All</button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import SmartTagz from 'smart-tagz'
const wishlistItems = ref([])
const updateWishlist = (items) => {
wishlistItems.value = items
}
const shareWishlist = () => {
const url = `https://example.com/wishlist/${encodeURIComponent(wishlistItems.value.join(','))}`
console.log('Share URL:', url)
}
const clearWishlist = () => {
if (confirm('Clear entire wishlist?')) {
wishlistItems.value = []
}
}
</script>
<style>
.wishlist {
max-width: 500px;
}
.full-notice {
color: #ea580c;
padding: 1rem;
background-color: #fed7aa;
border-radius: 0.375rem;
margin-top: 1rem;
}
.wishlist-actions {
margin-top: 1.5rem;
display: flex;
gap: 1rem;
}
button {
padding: 0.75rem 1rem;
border: none;
border-radius: 0.375rem;
cursor: pointer;
font-weight: 600;
flex: 1;
}
button:not(:disabled) {
background-color: #3b82f6;
color: white;
}
button:disabled {
background-color: #e5e7eb;
color: #9ca3af;
cursor: not-allowed;
}
</style>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
Live Demo - Build your wishlist (max 10 items):
My Wishlist
Your wishlist can hold up to 10 items. Try adding items and editing them!
Example 3: Image Tagging with Limit
vue
<template>
<div class="image-tagger">
<img :src="imageUrl" :alt="imageName" class="preview" />
<div class="tagger-section">
<h3>Tag This Image</h3>
<SmartTagz
:max-tags="8"
:sources="suggestedTags"
:allow-duplicates="false"
autosuggest
input-placeholder="Add tags..."
@on-changed="updateTags"
/>
<div class="tag-counter">
{{ imageTags.length }}/8 tags
</div>
</div>
<button @click="saveTags" :disabled="imageTags.length === 0" class="btn-save">
Save Tags
</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import SmartTagz from 'smart-tagz'
const imageUrl = 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=500&h=400&fit=crop'
const imageName = 'Mountain Landscape'
const suggestedTags = [
'Nature', 'Outdoor', 'Landscape', 'Sky', 'Mountains',
'Beach', 'Forest', 'Water', 'Sunset', 'Wildlife'
]
const imageTags = ref([])
const updateTags = (tags) => {
imageTags.value = tags
}
const saveTags = () => {
console.log('Saving tags:', imageTags.value)
// Save to API
}
</script>
<style>
.image-tagger {
max-width: 500px;
}
.preview {
width: 100%;
border-radius: 0.5rem;
margin-bottom: 1.5rem;
}
.tagger-section {
margin: 1.5rem 0;
}
.tagger-section h3 {
margin-bottom: 1rem;
}
.tag-counter {
margin-top: 0.75rem;
font-size: 0.875rem;
color: #6b7280;
}
.btn-save {
width: 100%;
padding: 0.75rem;
background-color: #10b981;
color: white;
border: none;
border-radius: 0.375rem;
cursor: pointer;
font-weight: 600;
}
.btn-save:disabled {
background-color: #e5e7eb;
color: #9ca3af;
cursor: not-allowed;
}
</style>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
Live Demo - Tag an image (max 8 tags):
Tag This Image
Add up to 8 tags to describe this image
Example 4: API Request with Tag Limits
vue
<template>
<form @submit.prevent="submitRequest">
<div class="form-group">
<label>Request Tags (max 5)</label>
<SmartTagz
:max-tags="5"
:sources="tagCategories"
:editable="true"
autosuggest
@on-changed="formData.tags = $event"
/>
</div>
<div class="form-group">
<label>Description</label>
<textarea v-model="formData.description" required></textarea>
</div>
<div class="form-actions">
<button type="submit" class="btn-submit" :disabled="!canSubmit">
{{ isSubmitting ? 'Submitting...' : 'Submit Request' }}
</button>
<span v-if="!canSubmit" class="error-note">
Add at least 1 tag to submit
</span>
</div>
<div v-if="submitMessage" :class="['submit-message', submitStatus]">
{{ submitMessage }}
</div>
</form>
</template>
<script setup>
import { ref, computed } from 'vue'
import SmartTagz from 'smart-tagz'
const tagCategories = [
'Bug Report', 'Feature Request', 'Documentation',
'Performance', 'Security', 'Accessibility'
]
const formData = ref({
tags: [],
description: ''
})
const isSubmitting = ref(false)
const submitMessage = ref('')
const submitStatus = ref('')
const canSubmit = computed(() => {
return formData.value.tags.length > 0 && formData.value.description.trim() !== ''
})
// Fake API simulation
const mockApiCall = () => {
return new Promise((resolve) => {
setTimeout(() => {
const success = Math.random() > 0.1 // 90% success rate
resolve(success)
}, 1500)
})
}
const submitRequest = async () => {
isSubmitting.value = true
submitMessage.value = ''
try {
const success = await mockApiCall()
if (success) {
submitMessage.value = `✅ Request submitted with tags: ${formData.value.tags.join(', ')}`
submitStatus.value = 'success'
formData.value = { tags: [], description: '' }
} else {
submitMessage.value = '❌ Failed to submit request. Please try again.'
submitStatus.value = 'error'
}
} catch (error) {
submitMessage.value = '❌ Network error. Please try again.'
submitStatus.value = 'error'
} finally {
isSubmitting.value = false
setTimeout(() => {
submitMessage.value = ''
}, 4000)
}
}
</script>
<style>
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
font-weight: 600;
margin-bottom: 0.5rem;
}
textarea {
width: 100%;
padding: 0.75rem;
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
font-family: inherit;
min-height: 100px;
resize: vertical;
}
.form-actions {
display: flex;
align-items: center;
gap: 1rem;
margin-top: 2rem;
}
.btn-submit {
padding: 0.75rem 1.5rem;
background-color: #3b82f6;
color: white;
border: none;
border-radius: 0.375rem;
cursor: pointer;
font-weight: 600;
}
.btn-submit:disabled {
background-color: #e5e7eb;
color: #9ca3af;
cursor: not-allowed;
}
.error-note {
color: #dc2626;
font-size: 0.875rem;
}
.submit-message {
margin-top: 1rem;
padding: 0.75rem 1rem;
border-radius: 0.375rem;
font-weight: 500;
}
.submit-message.success {
background-color: #d1fae5;
color: #065f46;
border: 1px solid #6ee7b7;
}
.submit-message.error {
background-color: #fee2e2;
color: #991b1b;
border: 1px solid #fecaca;
}
</style>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
Live Demo - Submit a request with tags:
Behavior Notes
Input Field Visibility
- Hidden when: Tag count >= maxTags
- Visible when: Tag count < maxTags
- Reappears: When tags are removed below limit
Error Message
When user tries to exceed limit:
"Maximum X tags allowed"
Auto-dismisses after 4 seconds1
2
2
Paste Operation
When pasting exceeds limit:
- Only tags up to limit are added
- Excess tags ignored
- Error message shown for overflow
- User can paste remaining items after removing tags
ARIA Label
Input field ARIA label updates:
"Add tags (2 of 5)" <- 2 tags added, max is 5
"Add tags (5 of 5)" <- at limit
"Add tags (0 of 5)" <- cleared1
2
3
2
3
Accessibility
- ARIA labels show progress: "X of Y"
- Tag limit shown in label
- Input hidden at limit, announced to screen readers
- Error messages announced to screen readers
TypeScript Support
typescript
interface SmartTagzProps {
maxTags?: number
allowDuplicates?: boolean
allowPaste?: { delimiter: string }
}1
2
3
4
5
2
3
4
5