Comments - Users and mentions

When a comment is posted, Liveblocks doesn’t store any user metadata, for example their avatar or name. Instead, it only saves their user ID, which you manually set when authenticating. To fetch user metadata, we provide functions that allow you to return the correct data.

Thread with resolved users

Authenticate your users

You can set a user’s ID when authenticating your application, for example with liveblocks.prepareSession. This ID is then used inside Comments to represent the current user, for example we’re using an email address as a user ID below.

const session = liveblocks.prepareSession("charlie.layne@example.com", {  userInfo: {    // Custom user info to be used in Presence    // This is NOT used in Comments    // ...  },});

There are two different authentication methods—make sure to follow an authentication guide for your framework to get started.

Adding user information

To add names and avatars to Comments, you need to convert user IDs into user objects. For example, here’s a userIds array and the information you need to return. You should return the same number of users as the number of user IDs, in the same order.

# If this is `userIds`["marc@example.com", "pierre@example.com"]
# Return `users`[{ name: "Marc", avatar: "https://example.com/marc.png" }, { name: "Pierre", avatar: "https://example.com/pierre.png" }]

To do this, add a property named resolveUsers to your LiveblocksProvider where you can return this information.

<LiveblocksProvider  authEndpoint="/api/liveblocks-auth"  resolveUsers={async ({ userIds }) => {    // ["marc@example.com", ...]    console.log(userIds);
// Get users from your back-end const users = await (userIds);
// [{ name: "Marc", avatar: "https://example.com/marc.png" }, ...] console.log(users);
// Return a list of users return users; }}>

Custom user information

The name, and avatar are handled by the default components, but you can also return custom metadata here. For example, each user may have a color property. You can retrieve these properties in your app with useUser.

function Component() {  const { user } = useUser("marc@example.com");
// { color: "red", name: "Marc", avatar: "https://example.com/marc.png" } console.log(user);
// ...}

Mentions

Comments allows you to use mentions/tag users by typing the @ character. You can also tag groups, for example @everyone. After a user’s mentioned in a thread, they’ll receive an inbox notification.

Working Comments mentions

Resolving mention suggestions

To define which mentions appear in the suggestion pop-up, you need to return a list of users that are being searched for. When a user types @ Comments passes you a text property, the text that the user has typed so far, which you can use to find and return matching user IDs.

# If "@mar" has been typed, this is `text`"mar"
# Return matching `userIds` and `groupIds`["marc@example.com", "marissa@example.com"]

You can resolve these search results by adding a resolveMentionSuggestions property to your LiveblocksProvider. Here’s what the function might look like if the user has typed "@mar" into the input.

<LiveblocksProvider  authEndpoint="/api/liveblocks-auth"  resolveUsers={async ({ userIds }) => {    // ...  }}  resolveMentionSuggestions={async ({ text, roomId }) => {    // "mar"    console.log(text);
// Return an array of user IDs for the query "mar" let userIds;
if (text) { // If there's a query, get user IDs from your back-end that match userIds = await (text); } else { // If there's no query, get all of the room's user IDs userIds = await (); }
// ["marc@example.com", "marissa@example.com"] console.log(userIds); return userIds; }}>

You can also resolve group mentions in here, though that requires you to structure the function differently.

Group mentions

Alongside user mentions, you can also mention groups by typing the @ character, for example @everyone or @engineering.

Working Comments group mentions

There are two different ways to set up group mentions:

  1. Passing group members on the front-end, particularly helpful for global mentions like @everyone and @here.
  2. Using managed groups of users on the back-end, helpful for handling specific groups of users like @engineering or @design.

Return a different format

When using group mentions, you must return a list of mention objects with a kind property, instead of a list user ID strings, like below.

- return ["marc@example.com"];+ return [+  {+    kind: "user",+    id: "marc@example.com",+  },+ ];

Users have a user kind and groups have group kind.

return [  {    kind: "user",    id: "marc@example.com",  },  {    kind: "group",    id: "engineering",  },];

Passing group members on the front-end

The easiest way to create a group mention is to pass a list of group members to the resolveMentionSuggestions function. This allows you to suggest a list of users that are part of the group, and requires no back-end code, simply return a list of user IDs. In the example below, a @everyone suggestion is added, which will tag all users in the app.

resolveMentionSuggestions={async ({ text, roomId }) => {  const allUserIds = await ();  const queryUserIds = await (text);
// `@everyone`, `@here`, etc. const globalMentions: MentionData[] = [];
// Create `@everyone` suggestion, for all users in app globalMentions.push({ kind: "group", id: "everyone", userIds: allUserIds, // Array of all user IDs });
return [ ...globalMentions, ...queryUserIds.map((user) => ({ kind: "user", id: user.id, })), ];}}

In the example above, @everyone will always be displayed in the suggestions list. Make sure to filter using the search text if you’d like to only show it when the user is typing "@everyone".

import type { MentionData } from "@liveblocks/client";
resolveMentionSuggestions={async ({ text, roomId }) => { const allUserIds = await (); const queryUserIds = await (text);
// `@everyone`, `@here`, etc. const globalMentions: MentionData[] = [];
// Show `@everyone` suggestion when users search for it if ("everyone".includes(text.toLowerCase())) { globalMentions.push({ kind: "group", id: "everyone", userIds: allUserIds, // Array of all user IDs }); }
return [ ...globalMentions, ...queryUserIds.map((user) => ({ kind: "user", id: user.id, })), ];}}

This method is generally most useful for dynamically fetching group mentions, for example with @everyone, @here, @online, but you can use it to create fixed group mentions as well.

Managed groups on the back-end

You can create mention groups on the back-end using the Node.js SDK or REST API, and they’ll be saved permanently. You can add new members, create new groups, list groups, and more. If your app has teams or groups of users, it makes sense to synchronize them with Liveblocks whenever there’s a change.

To get started, create a group using liveblocks.createGroup, defining each member by their user ID.

const group = await liveblocks.createGroup({  groupId: "engineering-team",  memberIds: ["marc@example.com", "florent@example.com"],});

In your resolveMentionSuggestions function, you can then query all the groups you’ve created for the current search text, and return them.

resolveMentionSuggestions={async ({ text, roomId }) => {  const groupIds = await queryGroupIds(text);  const userIds = await (text);
return [ ...groupIds.map((group) => ({ kind: "group", id: group.id, })), ...userIds.map((user) => ({ kind: "user", id: user.id, })), ];}}

Here’s how your queryGroupIds function can use liveblocks.getGroups to query all groups and filter them by the search text.

queryGroupIds.ts
"use server";
export async function queryGroupIds(text: string) { const { data: groups } = await liveblocks.getGroups();
return groups .filter((group) => group.id.includes(text)) .map((group) => group.id);}

If your app has multi-tenancy, make sure you handle permissions correctly, and filter out any groups that the user doesn’t have access to.

First-party multi-tenancy

Using the Liveblocks tenants feature, you can create groups with the same ID in different tenants. This is an easy way to compartmentalize resources in your app for each organization. In the snippet below, both organizations have an engineering team, but because seperate tenants are used, they are different groups.

const group = await liveblocks.createGroup({  groupId: "engineering-team",  memberIds: ["marc@example.com", "florent@example.com"],  tenantId: "apple-corp",});
const group = await liveblocks.createGroup({ groupId: "engineering-team", memberIds: ["chris@example.com", "olivier@example.com"], tenantId: "banana-inc",});

Adding group information

Similar to when displaying user information,

You can also add groups to Comments, allowing you to tag a team of people with one mention. Similar to users, group information is retrieved from a list of group IDs. You should return the same number of groups as the number of group IDs, in the same order.

# If this is `groupIds`["engineering", "design"]
# Return `groups`[{ name: "Engineering", avatar: "https://example.com/engineering.png" }, { name: "Design", avatar: "https://example.com/design.png" }]

Add a property named resolveGroupsInfo to your LiveblocksProvider where you can return this information.

<LiveblocksProvider  authEndpoint="/api/liveblocks-auth"  resolveGroupsInfo={async ({ groupIds }) => {    // ["engineering", ...]    console.log(groupIds);
// Get groups from your back-end const groups = await (groupIds);
// [{ name: "Engineering", avatar: "https://example.com/engineering.png" }, ...] console.log(groups);
// Return a list of groups return groups; }}>

The name and avatar are handled by the default components, but you can also return custom metadata here. For example, each group may have a color property. You can retrieve these properties in your app with useGroupInfo.

function Component() {  const { group } = useGroupInfo("engineering");
// { color: "blue", name: "Engineering", avatar: "https://example.com/engineering.png" } console.log(group);
// ...}

We use cookies to collect data to improve your experience on our site. Read our Privacy Policy to learn more.