Components
"use client";
import * as Mention from "@diceui/mention";
import * as React from "react";
const users = [
{
id: "1",
name: "Olivia Martin",
email: "[email protected]",
},
{
id: "2",
name: "Isabella Nguyen",
email: "[email protected]",
},
{
id: "3",
name: "Emma Wilson",
email: "[email protected]",
},
{
id: "4",
name: "Jackson Lee",
email: "[email protected]",
},
{
id: "5",
name: "William Kim",
email: "[email protected]",
},
];
export function MentionDemo() {
return (
<Mention.Root className="w-full max-w-[400px] **:data-tag:rounded **:data-tag:bg-blue-200 **:data-tag:py-px **:data-tag:text-blue-950 dark:**:data-tag:bg-blue-800 dark:**:data-tag:text-blue-50">
<Mention.Label className="font-medium text-sm text-zinc-950 leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-zinc-50">
Mention users
</Mention.Label>
<Mention.Input
placeholder="Type @ to mention someone..."
className="flex min-h-[60px] w-full rounded-md border border-zinc-200 bg-transparent px-3 py-2 text-base shadow-xs placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-zinc-800 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:border-zinc-800 dark:focus-visible:ring-zinc-300"
asChild
>
<textarea />
</Mention.Input>
<Mention.Portal>
<Mention.Content className="data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 min-w-[var(--dice-anchor-width)] overflow-hidden rounded-md border border-zinc-200 bg-white p-1 text-zinc-950 shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50">
{users.map((user) => (
<Mention.Item
key={user.id}
value={user.name}
className="relative flex w-full cursor-default select-none flex-col rounded-sm px-2 py-1.5 text-sm outline-hidden data-disabled:pointer-events-none data-highlighted:bg-zinc-100 data-highlighted:text-zinc-900 data-disabled:opacity-50 dark:data-highlighted:bg-zinc-800 dark:data-highlighted:text-zinc-50"
>
<span className="text-sm">{user.name}</span>
<span className="text-muted-foreground text-xs">
{user.email}
</span>
</Mention.Item>
))}
</Mention.Content>
</Mention.Portal>
</Mention.Root>
);
}
Installation
npm install @diceui/mention
Installation with shadcn/ui
CLI
npx shadcn@latest add "https://diceui.com/r/mention"
Manual
Install the following dependencies:
npm install @diceui/mention
Copy and paste the following code into your project.
import * as MentionPrimitive from "@diceui/mention";
import type * as React from "react";
import { cn } from "@/lib/utils";
function Mention({
className,
...props
}: React.ComponentProps<typeof MentionPrimitive.Root>) {
return (
<MentionPrimitive.Root
data-slot="mention"
className={cn(
"**:data-tag:rounded **:data-tag:bg-blue-200 **:data-tag:py-px **:data-tag:text-blue-950 dark:**:data-tag:bg-blue-800 dark:**:data-tag:text-blue-50",
className,
)}
{...props}
/>
);
}
function MentionLabel({
className,
...props
}: React.ComponentProps<typeof MentionPrimitive.Label>) {
return (
<MentionPrimitive.Label
data-slot="mention-label"
className={cn("px-0.5 py-1.5 font-semibold text-sm", className)}
{...props}
/>
);
}
function MentionInput({
className,
...props
}: React.ComponentProps<typeof MentionPrimitive.Input>) {
return (
<MentionPrimitive.Input
data-slot="mention-input"
className={cn(
"flex w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
{...props}
/>
);
}
function MentionContent({
className,
children,
...props
}: React.ComponentProps<typeof MentionPrimitive.Content>) {
return (
<MentionPrimitive.Portal>
<MentionPrimitive.Content
data-slot="mention-content"
className={cn(
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in",
className,
)}
{...props}
>
{children}
</MentionPrimitive.Content>
</MentionPrimitive.Portal>
);
}
function MentionItem({
className,
children,
...props
}: React.ComponentProps<typeof MentionPrimitive.Item>) {
return (
<MentionPrimitive.Item
data-slot="mention-item"
className={cn(
"relative flex w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-50",
className,
)}
{...props}
>
{children}
</MentionPrimitive.Item>
);
}
export { Mention, MentionContent, MentionInput, MentionItem, MentionLabel };
Layout
Import the parts, and compose them together.
import * as Mention from "@diceui/mention";
<Mention.Root>
<Mention.Label />
<Mention.Input />
<Mention.Portal>
<Mention.Content>
<Mention.Item />
</Mention.Content>
</Mention.Portal>
</Mention.Root>
Examples
Custom Trigger
"use client";
import { Textarea } from "@/components/ui/textarea";
import {
Mention,
MentionContent,
MentionInput,
MentionItem,
} from "@/components/ui/mention";
const users = [
{
id: "1",
name: "Olivia Martin",
email: "[email protected]",
},
{
id: "2",
name: "Isabella Nguyen",
email: "[email protected]",
},
{
id: "3",
name: "Emma Wilson",
email: "[email protected]",
},
{
id: "4",
name: "Jackson Lee",
email: "[email protected]",
},
{
id: "5",
name: "William Kim",
email: "[email protected]",
},
];
export function MentionCustomTriggerDemo() {
return (
<Mention trigger="#" className="w-full max-w-[400px]">
<MentionInput placeholder="Type # to mention a user..." asChild>
<Textarea />
</MentionInput>
<MentionContent>
{users.map((user) => (
<MentionItem
key={user.id}
value={user.name}
className="flex-col items-start gap-0.5"
>
<span className="text-sm">{user.name}</span>
<span className="text-muted-foreground text-xs">{user.email}</span>
</MentionItem>
))}
</MentionContent>
</Mention>
);
}
With Custom Filter
"use client";
import * as React from "react";
import { Textarea } from "@/components/ui/textarea";
import {
Mention,
MentionContent,
MentionInput,
MentionItem,
} from "@/components/ui/mention";
const commands = [
{
id: "1",
name: "help",
description: "Show available commands",
},
{
id: "2",
name: "clear",
description: "Clear the console",
},
{
id: "3",
name: "restart",
description: "Restart the application",
},
{
id: "4",
name: "reload",
description: "Reload the current page",
},
{
id: "5",
name: "quit",
description: "Exit the application",
},
];
export function MentionCustomFilterDemo() {
const [value, setValue] = React.useState<string[]>([]);
const [inputValue, setInputValue] = React.useState("");
// Custom filter that matches commands starting with the search term
function onFilter(options: string[], term: string) {
return options.filter((option) =>
option.toLowerCase().startsWith(term.toLowerCase()),
);
}
return (
<Mention
value={value}
onValueChange={setValue}
inputValue={inputValue}
onInputValueChange={setInputValue}
trigger="/"
onFilter={onFilter}
className="w-full max-w-[400px]"
>
<MentionInput placeholder="Type / to use a command..." asChild>
<Textarea />
</MentionInput>
<MentionContent>
{commands.map((command) => (
<MentionItem
key={command.id}
label={command.name}
value={command.name}
>
<span className="font-mono text-sm">{command.name}</span>
<span className="text-muted-foreground text-xs">
{command.description}
</span>
</MentionItem>
))}
</MentionContent>
</Mention>
);
}
API Reference
Root
The container for all mention parts. Mention tags can be styled using the data-tag
attribute within the root.
Prop
Type
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-disabled] | Present when disabled. |
Label
An accessible label that describes the mention input. Associates with the input element for screen readers.
Prop
Type
Input
The text input field that users can type into to trigger mentions.
Prop
Type
Portal
A portal for rendering the mention content outside of its DOM hierarchy.
Prop
Type
Content
The popover container for mention items. Positions the mention popover relative to the cursor position.
Prop
Type
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "top" | "right" | "bottom" | "left" |
[data-align] | "start" | "center" | "end" |
CSS Variable | Description |
---|---|
--dice-transform-origin | Transform origin for cursor positioning. |
--dice-available-width | Available width in the viewport for the popover element. |
--dice-available-height | Available height in the viewport for the popover element. |
Item
An interactive option in the mention list.
Prop
Type
Data Attribute | Value |
---|---|
[data-highlighted] | Present when the item is highlighted. |
[data-disabled] | Present when the item is disabled. |
[data-value] | The value of the item. |
Accessibility
Keyboard Interactions
Key | Description |
---|---|
Enter | When open, selects the highlighted mention option. |
ArrowUp | When open, highlights the previous mention option. |
ArrowDown | When open, highlights the next mention option. |
Home | When open, highlights the first mention option. |
End | When open, highlights the last mention option. |
Escape | Closes the mention popover and returns focus to the input. |