> For AI agents: the complete Sanity documentation index is available at [https://www.sanity.io/docs/llms.txt](https://www.sanity.io/docs/llms.txt).

# Enable mentions

Turn on the mention picker by giving the comments provider a list of users, and query for the mentions that result.

The mention picker is off until you give the provider a list of users who can be mentioned. This guide turns it on, and covers how to query for the mentions that result.

Prerequisites:

- A working comments panel. See [Get started](https://www.sanity.io/docs/collaboration/get-started).
- Read access to the project's users. `useUsers()` reads them by default. For the organization's users instead, call `useUsers({resourceType: 'organization', organizationId})`.

## Build the mention options

Each option is a user, plus one flag:

```typescript
interface MentionOption {
  id: string
  displayName?: string
  imageUrl?: string
  /** `false` renders the option as unavailable. */
  granted?: boolean
}
```

There is no mention-fetching hook in `@sanity/collaboration`: which users may be mentioned on a document is a permissions question, and it belongs in the SDK. Until a permission-aware read exists, map `useUsers`. Note the shape change, since `sanityUserId` becomes `id` and the profile is flattened:

**useMentionOptions.ts**

```typescript
import type {MentionOption} from '@sanity/collaboration'
import {useUsers} from '@sanity/sdk-react'
import {useMemo} from 'react'

export function useMentionOptions(): MentionOption[] {
  const {data} = useUsers()

  return useMemo(
    () =>
      data.map((user) => ({
        id: user.sanityUserId,
        displayName: user.profile.displayName,
        imageUrl: user.profile.imageUrl,
      })),
    [data],
  )
}
```

## Pass the mention options to the provider

`SDKCommentsProvider` and `CommentsProvider` both take `mentionOptions`, and every `CommentInput` below it gets the picker, including the reply composer inside a thread card and the inline editor a comment opens for editing.

```tsx
import {SDKCommentsProvider} from '@sanity/collaboration/sdk'

import {useMentionOptions} from './useMentionOptions'

// Inside a component:
const mentionOptions = useMentionOptions()

<SDKCommentsProvider mentionOptions={mentionOptions}>
  <Panel />
</SDKCommentsProvider>
```

> [!WARNING]
> Memoize the array, as `useMentionOptions` does with `useMemo`. It is context, so a new identity on every render re-renders every comment under the provider.

Typing `@` in the composer now opens the picker.

## Mark users who may not be mentioned

`useUsers` returns the users on a resource, not the subset with permission to see a given document. When you know that difference, `granted: false` renders an option as unavailable rather than hiding it, which is the affordance a permission-aware read will plug into:

```typescript
// `canSeeDocument` is your app's own permission check.
const mentionOptions = users.map((user) => ({
  id: user.sanityUserId,
  displayName: user.profile.displayName,
  granted: canSeeDocument(user.sanityUserId),
}))
```

Hiding the user instead is also valid, and is a filter on the same array. The difference is whether the author learns that the person exists but cannot be added.

## Query for mentions

A mention is an inline object inside a message's Portable Text, carrying a `userId` and nothing else. Mentions live inside `children`, not at block level, so a "mentions me" filter has to descend:

```groq
count(message[].children[_type == "mention" && userId == $userId]) > 0
```

That filter with `useCommentsQuery` is a mentions inbox:

**MentionsInbox.tsx**

```tsx
import {Comment, useCommentsContext} from '@sanity/collaboration/ui'
import {useCommentsQuery} from '@sanity/sdk-react/collaboration'

export function MentionsInbox() {
  const {currentUser} = useCommentsContext()
  const {comments} = useCommentsQuery({
    filter:
      'count(message[].children[_type == "mention" && userId == $userId]) > 0',
    params: {userId: currentUser.id},
  })

  return (
    <ul>
      {comments.map((comment) => (
        <li key={comment.id}>
          <p>
            {comment.documentType} · {comment.documentId} · {comment.fieldPath}
          </p>
          <Comment comment={comment} />
        </li>
      ))}
    </ul>
  )
}
```

It returns comments rather than threads, so the surface renders `Comment` rather than `CommentThread`. The query reaches comments across the organization, on documents in any dataset, so each result is labeled with its document type, document ID, and field path. `Comment` with no callbacks renders no actions, which keeps the inbox read-only: the writes from `useCommentActions` target the app's own dataset, which a comment from another dataset is not in. To keep the inbox to the app's dataset, add the dataset prefix condition from [Scope to one dataset](https://www.sanity.io/docs/collaboration/sdk-query-comments) to the filter.

The worked version is an example you can run: [mentions](https://github.com/sanity-io/collaboration-platform-examples/tree/main/examples/mentions).

To find mentions while walking a message yourself, look for inline objects with `_type: 'mention'` in each block's `children`. The mentioned user is `userId`.

## Next steps

- How a mention resolves to a name and an avatar: [Users and identity](https://www.sanity.io/docs/collaboration/users-and-identity).
- Which read to reach for on a cross-document surface: [Comment data flow](https://www.sanity.io/docs/collaboration/data-flow).
- Every helper and type involved: [Helpers](https://collaboration-platform.sanity.dev/?path=/docs/helpers--docs).

