| name | n-mention |
| description | A mention input component for @mentions with dropdown suggestions, supporting both text and textarea modes |
| author | jiaiyan |
| version | 1.0.0 |
n-mention Component
The n-mention component is an input field that supports @mentions with a dropdown suggestion list. It's useful for mentioning users, tags, or other entities in text content with autocomplete functionality.
When to Use
Use n-mention when you need to:
- Implement @mentions for users in comments or messages
- Create hashtag or tag inputs with autocomplete
- Build rich text inputs with entity references
- Provide autocomplete suggestions while typing
- Create mention functionality in chat applications
Basic Usage
Basic Mention Input
<template>
<n-mention :options="options" default-value="@" />
</template>
<script setup>
const options = [
{ label: 'John Doe', value: 'john' },
{ label: 'Jane Smith', value: 'jane' },
{ label: 'Bob Wilson', value: 'bob' }
]
</script>
Textarea Mode
<template>
<n-mention type="textarea" :options="options" />
</template>
Autosize Textarea
<template>
<n-mention type="textarea" :options="options" autosize />
</template>
API Reference
Props
| Name | Type | Default | Description |
|---|
value / v-model:value | string | null | undefined | Manually set input value. |
default-value | string | '' | Default value when not manually set. |
type | 'text' | 'textarea' | 'text' | Input type. |
options | MentionOption[] | [] | Mention options list. |
prefix | string | string[] | '@' | Prefix character(s) to trigger mentions. Must be exactly 1 character each. |
separator | string | ' ' | Character to split mentions. Must be exactly 1 character. |
placeholder | string | '' | Placeholder. |
disabled | boolean | false | Whether to disable the input. |
bordered | boolean | true | Whether to display the border. |
size | 'small' | 'medium' | 'large' | 'medium' | Input size. |
loading | boolean | false | Whether the selection panel is loading. |
autosize | boolean | { maxRows?: number, minRows?: number } | false | Autosize for textarea. |
|
MentionOption Properties
| Name | Type | Description |
|---|
label | string | (option: MentionOption) => VNodeChild | Option label. |
value | string | Option value. Must be unique. |
disabled | boolean | Option disabled state. |
class | string | Option class property. |
style | string | Option style property. |
render | (option: MentionOption) => VNodeChild | Custom render function. |
Events
| Name | Parameters | Description |
|---|
update:value | (value: string) => void | Triggered when value changes. |
update:show | (show: boolean) => void | Callback when panel is shown or hidden. |
focus | (e: FocusEvent) => void | Triggered when input is focused. |
blur | (e: FocusEvent) => void | Triggered when input loses focus. |
search | (pattern: string, prefix: string) => void | Triggered when searching. |
select | (option: MentionOption, prefix: string) => void | Triggered when option is selected. |
Slots
| Name | Parameters | Description |
|---|
empty | () | Slot when menu has no data. |
Methods
| Name | Type | Description |
|---|
focus | () => void | Manually focus the component. |
blur | () => void | Manually blur the component. |
Common Patterns
Load Remote Options
<template>
<n-mention
:options="options"
:loading="loading"
@search="handleSearch"
/>
</template>
<script setup>
import { ref } from 'vue'
const options = ref([])
const loading = ref(false)
const handleSearch = async (pattern, prefix) => {
loading.value = true
try {
const response = await fetch(`/api/users?q=${pattern}`)
const users = await response.json()
options.value = users.map(user => ({
label: user.name,
value: user.username
}))
} finally {
loading.value = false
}
}
</script>
Custom Render Label
<template>
<n-mention :options="options" :render-label="renderLabel" />
</template>
<script setup>
import { h } from 'vue'
const options = [
{ label: 'John Doe', value: 'john', avatar: 'john.jpg' },
{ label: 'Jane Smith', value: 'jane', avatar: 'jane.jpg' }
]
const renderLabel = (option) => {
return h('div', { style: 'display: flex; align-items: center; gap: 8px;' }, [
h('img', { src: option.avatar, style: 'width: 24px; height: 24px; border-radius: 50%;' }),
option.label
])
}
</script>
Multiple Trigger Prefixes
<template>
<n-mention :options="options" :prefix="['@', '#']" @search="handleSearch" />
</template>
<script setup>
import { ref } from 'vue'
const options = ref([])
const handleSearch = (pattern, prefix) => {
if (prefix === '@') {
options.value = getUserOptions(pattern)
} else if (prefix === '#') {
options.value = getTagOptions(pattern)
}
}
</script>
Work with Form
<template>
<n-form ref="formInstRef" :model="formModel" :rules="rules">
<n-form-item label="Comment" path="comment">
<n-mention v-model:value="formModel.comment" :options="options" type="textarea" />
</n-form-item>
<n-button @click="handleButtonClick">Submit</n-button>
</n-form>
</template>
<script setup>
import { ref } from 'vue'
const formInstRef = ref(null)
const formModel = ref({
comment: ''
})
const rules = {
comment: {
required: true,
message: 'Please enter a comment'
}
}
const handleButtonClick = () => {
formInstRef.value?.validate()
}
</script>
Manual Focus/Blur Control
<template>
<n-space>
<n-mention ref="myMention" :options="options" />
<n-button @click="triggerFocus">Focus</n-button>
</n-space>
</template>
<script setup>
import { ref } from 'vue'
const myMention = ref(null)
const triggerFocus = () => {
myMention.value?.focus()
setTimeout(() => {
myMention.value?.blur()
}, 1000)
}
</script>
Custom Filter Function
<template>
<n-mention :options="options" :filter="customFilter" />
</template>
<script setup>
const customFilter = (pattern, option) => {
return option.value.toLowerCase().includes(pattern.toLowerCase())
}
</script>
Validation Status
<template>
<n-space vertical>
<n-mention status="warning" placeholder="Warning state" />
<n-mention status="error" placeholder="Error state" />
<n-mention status="success" placeholder="Success state" />
</n-space>
</template>
Best Practices
-
Use search event for async data: Load options asynchronously when the user types to improve performance.
-
Set loading during async operations: Show loading state while fetching remote options.
-
Use render-label for rich option display: Display avatars or additional info in the dropdown.
-
Configure prefix for different mention types: Use multiple prefixes (@ for users, # for tags, etc.).
-
Set autosize for textarea mode: Enable autosize for better UX in comment/message inputs.
-
Use filter for custom matching logic: Implement custom filtering when default behavior doesn't fit your needs.
-
Handle select event for tracking mentions: Track which mentions were selected for your application logic.