Sign in

Share dialog

Build a share dialog like the ones in Notion or Figma with Liveblocks. Invite people to a document by email, give them viewer, commenter, or editor roles, share with whole teams at once, toggle public link access, list everyone with access, and notify people when a document is shared with them.

Share dialog

Share dialog in the Next.js Starter Kit

Features

Get started

A share dialog is built on ID token authentication, where permissions are stored on each room.

Implementation

This is an overview of how each feature can be implemented. Each document is a room in your Liveblocks app, and with ID token authentication the room itself stores who can access it. A share dialog is a UI over these room accesses. When a user invites someone or changes a role, the dialog calls your server, which updates the room with @liveblocks/node.

Room permissions

Each room holds permissions at three levels: defaultAccesses for everyone, groupsAccesses for teams, and usersAccesses for individuals. Create documents as private by default, with only the creator having access, using Liveblocks.createRoom.

import { Liveblocks } from "@liveblocks/node";
const liveblocks = new Liveblocks({ secret: process.env.LIVEBLOCKS_SECRET_KEY!,});
await liveblocks.createRoom("document-a", { // Private, no access by default defaultAccesses: [],
// The creator has full access usersAccesses: { "olivier@example.com": ["*:write"], },});

Users are matched by the userId and groupIds set when authenticating them, so no Liveblocks-specific accounts are needed.

Inviting users

When a user submits an email in the share dialog, your server grants access with Liveblocks.updateRoom. Only the accesses you pass are changed, and existing members keep theirs. Set a user’s access to null to remove them.

await liveblocks.updateRoom("document-a", {  usersAccesses: {    // Invite Stacy as an editor    "stacy@example.com": ["*:write"],
// Remove Marc’s access "marc@example.com": null, },});

Connected users are affected immediately, and someone whose access is removed is disconnected from the room.

Roles

Map your dialog’s roles to permission scopes. A viewer gets read access, a commenter can also join discussions, and an editor can change everything.

const ROLES = {  viewer: ["*:read"],  commenter: ["*:read", "comments:write"],  editor: ["*:write"],};
await liveblocks.updateRoom("document-a", { usersAccesses: { "stacy@example.com": ROLES.commenter, },});

More granular scopes exist too, such as storage:read and feeds:write. Learn more under Permissions.

Team sharing

Share a document with a whole team at once using groupsAccesses. Groups are custom strings that you attach to users with groupIds during authentication, for example each user’s departments or workspaces.

await liveblocks.updateRoom("document-a", {  groupsAccesses: {    // Everyone in "engineering" can edit    engineering: ["*:write"],  },});

Anyone authenticated with the engineering group ID can now open the document, including people who join the team later. In multi-tenant apps, use organizations to keep each workspace’s rooms and users separate.

Public link access

An “anyone with the link” toggle maps to the room’s defaultAccesses. Keep the array empty for private documents, and add read or write access to open them up.

// Anyone with the link can viewawait liveblocks.updateRoom("document-a", {  defaultAccesses: ["*:read"],});
// Back to private, invited members keep their accessawait liveblocks.updateRoom("document-a", { defaultAccesses: [],});

User and group accesses always override the default, so making a document private never locks out invited members.

Listing who has access

Render the dialog’s member list by reading the room’s accesses with Liveblocks.getRoom.

const room = await liveblocks.getRoom("document-a");
// { "olivier@example.com": ["*:write"], "stacy@example.com": ["*:read"] }console.log(room.usersAccesses);

On the client, resolve each user ID into a name and avatar with useUser, backed by the resolveUsers function you configure on LiveblocksProvider.

import { useUser } from "@liveblocks/react/suspense";
function Member({ userId }: { userId: string }) { const { user } = useUser(userId);
return ( <div> <img src={user.avatar} alt="" /> {user.name} </div> );}

Permission-aware UI

Inside the document, adapt the interface to the current user’s access with the canWrite and canComment properties on useSelf, for example hiding the toolbar from viewers, or only showing the share button to editors.

import { useSelf } from "@liveblocks/react/suspense";
function Toolbar() { const canWrite = useSelf((me) => me.canWrite);
return canWrite ? <EditorToolbar /> : <ViewerBadge />;}

Permissions are enforced on Liveblocks servers, so hiding UI is purely cosmetic, as read-only users can’t modify the document even with a modified client.

Invite notifications

Tell people when a document is shared with them by triggering a custom notification with Liveblocks.triggerInboxNotification from the same endpoint that grants access. Render it in an in-app inbox, or deliver it by email.

await liveblocks.triggerInboxNotification({  userId: "stacy@example.com",  kind: "$documentShared",  subjectId: "document-a",  activityData: {    title: "Launch plan",    sharedBy: "Olivier",  },});

Learn more under the inbox use case and our Notifications overview.

Examples

The Next.js Starter Kit contains a complete share dialog implementation, with user invites, roles, group sharing, and a private/public toggle.