Better Rich Text Buy licence

Users

On this page
  1. Setup
  2. User record
  3. How lookups behave
  4. API
  5. Which features use it
  6. Server endpoints

One function tells the editor who people are. Comments, suggested edits, revision history, live presence and mentions all read names, avatars, titles and colours from it, so you write the lookup once and every feature shows the same person the same way.

Setup

TypeScript
import { createRichTextEditor } from '@better-rich-text/core';
import { createUsersPlugin } from '@better-rich-text/users';

createRichTextEditor('#editor', {
  actorId: currentUser.id,
  plugins: [
    createUsersPlugin({
      currentUserId: currentUser.id,
      fetchUsers: async (ids, signal) => {
        const response = await fetch(`/api/users?ids=${ids.map(encodeURIComponent).join(',')}`, { signal });
        return response.json();
      },
      // Optional: powers @mentions in the document and in comment replies.
      searchUsers: async (query, signal) => {
        const response = await fetch(`/api/users/search?q=${encodeURIComponent(query)}`, { signal });
        return response.json();
      },
      // Optional: people you already have, so they render without a request.
      users: [currentUser],
    }),
    // comments, suggestions, revisions, mentions…
  ],
});

Set actorId to the same id so edits, comments and suggestions are stamped with it. With the collaboration server, the authenticated identity from your authenticate adapter wins.

User record

TypeScript
interface User {
  id: string;
  name: string;
  avatar?: string;       // https:// or data:image/…;base64; anything else is ignored
  description?: string;  // title, team or email, shown under the name
  color?: string;        // caret, highlight and avatar colour; derived from the id when absent
  custom?: Record<string, JSON>; // roles, profile links, anything else
}

fetchUsers returns the users it knows. Ids it leaves out are shown as "Unknown user".

How lookups behave

  • Batched. Ids requested in the same tick go out in one fetchUsers call (batchDelay, default 0 ms), split into chunks of maxBatch (default 100).

  • Cached. Each id is fetched once for the editor's lifetime, or until ttl milliseconds pass. invalidate(id) forces a refetch.

  • Never blocking. get(id) returns at once: the cached user, or a placeholder with pending: true while the request runs. Views repaint when records arrive.

  • Failure-safe. A rejected fetchUsers keeps placeholders, reports through onError, and retries on the next request.

API

TypeScript
const users = usersOf(editor); // UserDirectory | undefined

users.get('u-2');            // ResolvedUser, synchronously (may be a placeholder)
await users.lookup('u-2');   // ResolvedUser once fetched
await users.lookupMany(ids); // in the order given
await users.current();       // the current user
await users.search('sam');   // through searchUsers (empty when not configured)
users.prime([user]);         // add or replace records, for example from a server push
users.invalidate('u-2');     // refetch on next use
const stop = users.subscribe((arrived) => repaint(arrived));

ResolvedUser adds color (always set), initials (for avatars without images) and pending.

Commands (also available to AI agents as tools): users.lookup { ids } and users.current.

Hook: action users.resolved with the users that just arrived.

Which features use it

FeatureUses
Comments Author names, avatars and titles on threads and replies; @ search in reply boxes
Suggested edits Author names, colours for the suggestion highlight, review cards
Revision history The author recorded on new versions and the name shown in the list
Presence Caret labels, caret and selection colours, avatars in the collaborators bar
Mentions @ search in the document when no @ source is configured

Every feature works without the directory, using the names it stored when the comment, suggestion or version was made. Stored records always keep the user id, so names update everywhere when a person changes theirs.

Server endpoints

See Server for the endpoint shapes.