API Reference@liveblocks/emails

@liveblocks/emails provides a set of functions that simplifies sending styled emails with Notifications and webhooks. This library is only intended for use in your Node.js back end.

Requirements

@liveblocks/emails requires the @liveblocks/node package to be installed and for react to be a peer dependency in your project.

Setup

This package exposes functions that enable easy creation of styled emails with React and HTML. Each method is designed to be used with our webhooks which means you must set them up first. Webhooks require an API endpoint in your application, and this is typically what they will look like.

Next.js route handler for webhooks
import { isThreadNotificationEvent, WebhookHandler } from "@liveblocks/node";import { Liveblocks } from "@liveblocks/node";import { prepareThreadNotificationEmailAsReact } from "@liveblocks/emails";
const liveblocks = new Liveblocks({ secret: "",});
const webhookHandler = new WebhookHandler( process.env.LIVEBLOCKS_WEBHOOK_SECRET_KEY as string);
export async function POST(request: Request) { const body = await request.json(); const headers = request.headers;
// Verify if this is a real webhook request let event; try { event = webhookHandler.verifyRequest({ headers: headers, rawBody: JSON.stringify(body), }); } catch (err) { console.error(err); return new Response("Could not verify webhook call", { status: 400 }); }
// Using `@liveblocks/emails` to create an email if (isThreadNotificationEvent(event)) { const emailData = await prepareThreadNotificationEmailAsReact( liveblocks, event );
if (emailData.type === "unreadMention") { const email = ( <div> <div> @{emailData.comment.author.id} at {emailData.comment.createdAt} </div> <div>{emailData.comment.reactBody}</div> </div> );
// Send unread mention email // ... } }
return new Response(null, { status: 200 });}

We’ll only show the highlighted part below, as it’s assumed you’ve set this already, and your file contains liveblocks and event.

End-to-end guides

We have two guides that take you through every step of setting up your email notifications, including setting up webhooks:

Ready-made email templates

We have a number of examples that show you how to set up emails with your Comments or Text Editor application. Each Resend example has full ready-made email templates inside, which are a great starting point for your application.

Thread notification emails

These functions help you create emails to notify users of unread comments in threads. They fetch each relevant comment, filtering out any that have already been read, and help you style each comment’s body with either React or HTML.

An email showing 7 new comments, with comment bodies and links to each comment

The function also helps you distinguish between unread mentions and unread replies.

A thread has unread replies if a comment was created after the readAt date on the notification, and created before or at the same time as the notifiedAt date. All unread replies are returned in an array.

{  type: "unreadReplies",  roomId: "my-room-id",  comments: [    {/* Comment data */},    // ...  ],}

A thread has an unread mention if it has unread replies, and one of the replies mentions the user. A single comment with the latest mention is returned.

{  type: "unreadMentions",  roomId: "my-room-id",  comment: {/* Comment data */},}

prepareThreadNotificationEmailAsReact

Takes a thread notification webhook event and returns unread comment body(s) related to the notification, as React nodes. It can return one of three formats, an unreadMention type containing one comment, an unreadReplies type returning multiple comments, or null if there are no unread mentions/replies. You can also resolve user & room data and customize the components.

import { prepareThreadNotificationEmailAsReact } from "@liveblocks/emails";import { isThreadNotificationEvent } from "@liveblocks/node";
// Get `liveblocks` and `event` (see "Setup" section)// ...
if (isThreadNotificationEvent(event)) { const emailData = await prepareThreadNotificationEmailAsReact( liveblocks, event ); let email;
switch (emailData.type) { case "unreadMention": { email = ( <div> <div> @{emailData.comment.author.id} at {emailData.comment.createdAt} </div> <div>{emailData.comment.reactBody}</div> </div> ); break; }
case "unreadReplies": { email = ( <div> {emailData.comments.map((comment) => ( <div key={comment.id}> <div> @{comment.author.id} at {comment.createdAt} </div> <div>{comment.reactBody}</div> </div> ))} </div> ); break; } }}
// Send your email// ...

It’s designed to be used in a webhook event, which requires a Liveblocks Node.js client and a WebhookHandler. Check for the correct webhook event using isThreadNotificationEvent before running the function, such as in this Next.js route handler.

Returns
  • valueThreadNotificationEmailDataAsReact | null

    Returns comment information, and a formatted React body, ready for use in emails. Returns null if there are no unread mentions or replies. The result has two formats depending on whether this notification is for a single unread mention, or for multiple unread replies:

Arguments
  • clientLiveblocksRequired

    A Liveblocks Node.js client.

  • eventThreadNotificationEventRequired

    An object passed from a webhook event, specifically the ThreadNotificationEvent. Learn more about setting this up.

  • optionsobject

    A number of options to customize the format of the comments, adding user info, room info, and styles.

  • options.resolveUsers

    A function that resolves user information in Comments. Return an array of UserMeta["info"] objects in the same order they arrived. Works similarly to the resolver on the client. Learn more.

  • options.resolveRoomInfo

    A function that resolves room information. Return a RoomInfo object, as matching your types. Works similarly to the resolver on the client but for one room. Learn more.

  • options.componentsobject

    Pass different React components to customize the elements in the comment bodies. Five components can be passed to the object: Container, Paragraph, Text, Link, Mention. Learn more.

  • options.components.Container({ children: ReactNode }) => ReactNode

    The comment body container.

  • options.components.Paragraph({ children: ReactNode }) => ReactNode

    The paragraph block.

  • options.components.Text({ children: ReactNode }) => ReactNode

    The text element.

  • options.components.Link

    The link element.

  • options.components.Mention

    The mention element.

Resolving data

Similarly to on the client, you can resolve users and room info, making it easier to render your emails. For example, you can resolve a user’s ID into their name, and show their name in the email.

const emailData = await prepareThreadNotificationEmailAsReact(  liveblocks,  webhookEvent,  {    resolveUsers: async ({ userIds }) => {      const usersData = await (userIds);
return usersData.map((userData) => ({ name: userData.name, // "Nimesh" avatar: userData.avatar.src, // "https://..." })); }, resolveRoomInfo({ roomId }) { return { name: roomId, // "my-room-name" url: `https://example.com/${roomId}`, }; }, });
// { type: "unreadMention", comment: { ... }, ... }console.log(emailData);
// { name: "Nimesh", avatar: "https://..." }console.log(emailData.comment.author.info);
// { name: "my-room-name", url: "https://example.com/my-room-name" }console.log(emailData.roomInfo);

Customizing components

Each React component in the comment body can be replaced with a custom React component, if you wish to apply different styles. Five components are available: Container, Paragraph, Text, Link, Mention.

const emailData = await prepareThreadNotificationEmailAsReact(  liveblocks,  webhookEvent,  {    components: {      Paragraph: ({ children }) => <p>{children}</p>,
// `react-email` components are supported Text: ({ children }) => ( <Text className="text-sm text-black m-0 mb-4">{children}</Text> ),
// `user` is the optional data returned from `resolveUsers` Mention: ({ element, user }) => ( <span style={{ color: "red" }}>@{user?.name ?? element.id}</span> ),
// If the link is rich-text render it, otherwise use the URL Link: ({ element, href }) => <a href={href}>{element?.text ?? href}</a>, }, });
// { type: "unreadMention", comment: { ... }, ... }console.log(emailData);
// The components are now used in this React bodyconsole.log(emailData.comment.reactBody);

prepareThreadNotificationEmailAsHtml

Takes a thread notification webhook event and returns unread comment body(s) related to the notification, as an HTML-safe string. It can return one of three formats, an unreadMention type containing one comment, an unreadReplies type returning multiple comments, or null if there are no unread mentions/replies. You can also resolve user & room data and customize the styles.

import { prepareThreadNotificationEmailAsHtml } from "@liveblocks/emails";import { isThreadNotificationEvent } from "@liveblocks/node";
// Get `liveblocks` and `event` (see "Setup" section)// ...
if (isThreadNotificationEvent(event)) { const emailData = await prepareThreadNotificationEmailAsHtml( liveblocks, event ); let email;
switch (emailData.type) { case "unreadMention": { email = ` <div> <div> @${emailData.comment.author.id} at ${emailData.comment.createdAt} </div> <div>${emailData.comment.htmlBody}</div> </div> `; break; }
case "unreadReplies": { email = ` <div> ${emailData.comments .map( (comment) => ` <div> <div> @${comment.author.id} at ${comment.createdAt} </div> <div>${comment.htmlBody}</div> </div> ` ) .join("")} </div> `; break; } }}
// Send your email// ...

It’s designed to be used in a webhook event, which requires a Liveblocks Node.js client, a WebhookHandler. Check for the correct webhook event using isThreadNotificationEvent before running the function, such as in this Next.js route handler.

Returns
  • valueThreadNotificationEmailDataAsHtml | null

    Returns comment information, and a formatted HTML body, ready for use in emails. Returns null if there are no unread mentions or comments. The result has two formats depending on whether this notification is for a single unread mention, or for multiple unread replies:

Arguments
  • clientLiveblocksRequired

    A Liveblocks Node.js client.

  • eventThreadNotificationEventRequired

    An object passed from a webhook event, specifically the ThreadNotificationEvent. Learn more about setting this up.

  • optionsobject

    A number of options to customize the format of the comments, adding user info, room info, and styles.

  • options.resolveUsers

    A function that resolves user information in Comments. Return an array of UserMeta["info"] objects in the same order they arrived. Works similarly to the resolver on the client. Learn more.

  • options.resolveRoomInfo

    A function that resolves room information. Return a RoomInfo object, as matching your types. Works similarly to the resolver on the client but for one room. Learn more.

  • options.stylesobject

    Pass CSS properties to style the different HTML elements in the comment bodies. Five elements can be styled: paragraph, code, strong, link, mention. Learn more.

  • options.styles.containerCSSProperties

    Inline styles to apply to the comment body container.

  • options.styles.paragraphCSSProperties

    Inline styles to apply to the paragraph block.

  • options.styles.codeCSSProperties

    Inline styles to apply to the code element.

  • options.styles.strongCSSProperties

    Inline styles to apply to the strong element.

  • options.styles.mentionCSSProperties

    Inline styles to apply to the mention element.

  • options.styles.linkCSSProperties

    Inline styles to apply to the link element.

Resolving data

Similarly to on the client, you can resolve users and room info, making it easier to render your emails. For example, you can resolve a user’s ID into their name, and show their name in the email.

const emailData = await prepareThreadNotificationEmailAsHtml(  liveblocks,  webhookEvent,  {    resolveUsers: async ({ userIds }) => {      const usersData = await (userIds);
return usersData.map((userData) => ({ name: userData.name, // "Nimesh" avatar: userData.avatar.src, // "https://..." })); }, resolveRoomInfo({ roomId }) { return { name: roomId, // "my-room-name" url: `https://example.com/${roomId}`, }; }, });
// { type: "unreadMention", comment: { ... }, ... }console.log(emailData);
// { name: "Nimesh", avatar: "https://..." }console.log(emailData.comment.author.info);
// { name: "my-room-name", url: "https://example.com/my-room-name" }console.log(emailData.roomInfo);

Styling elements

Each element in the comment body can be styled with custom CSS properties, if you would like to change the appearance. Five elements are available: paragraph, code, strong, mention, link.

const emailData = await prepareThreadNotificationEmailAsHtml(  liveblocks,  webhookEvent,  {    styles: {      paragraph: { margin: "12px 0" },
mention: { fontWeight: "bold", color: "red", },
link: { textDecoration: "underline", }, }, });
// { type: "unreadMention", comment: { ... }, ... }console.log(emailData);
// The elements in the comment body are now styledconsole.log(emailData.comment.htmlBody);

Text Mention notification emails

These functions help you create emails to notify users when they have an unread mention in a Text Editor document. In this case, a mention is not related to comments, but is instead an inline mention inside the text editor itself. If the mention has not been read, the functions fetch a text mention and its surrounding text, giving you more context, and helping you style the mention content with either React or HTML.

An email showing a text mention in a text editor document

The functions helps to determine if the mention still exists in the document and will indicate that there’s no email to send in this case. Currently, only mentions in paragraph blocks create notifications, as there are limitations around retrieving mentions in plugins.

Limitations

Before you get started, there are some limitations with text mentions that you should be aware of.

Mentions in plugins

If a user is mentioned in a plugin or extension, a text mention notification is not sent. This is because Liveblocks doesn’t know the exact schema of your editor and all its plugins, and we can’t extract the data correctly. This means that only mentions in paragraph blocks are sent, and mentions in lists, checkboxes, etc., are not, as they are all powered by plugins. We’re investigating solutions for this, and we’d like to hear from you if you have any thoughts.

Multiple Tiptap editors

Tiptap optionally allows you to render multiple editors per page, instead of just one. For now, these functions only support one editor per room, but we’ll be looking to add support for more later.

prepareTextMentionNotificationEmailAsReact

Takes a text mention notification webhook event and returns an unread text mention with its surrounding text as React nodes. It can also return null if the text mention does not exist anymore or has been already been read. You can also resolve user & room data and customize the components.

import { prepareTextMentionNotificationEmailAsReact } from "@liveblocks/emails";import { isTextMentionNotificationEvent } from "@liveblocks/node";
// Get `liveblocks` and `event` (see "Setup" section)// ...
if (isTextMentionNotificationEvent(event)) { const emailData = await prepareTextMentionNotificationEmailAsReact( liveblocks, event );
const email = ( <div> <div> @{emailData.mention.author.id} at {emailData.mention.createdAt} </div> <div>{emailData.mention.reactContent}</div> </div> );}
// Send your email// ...

It’s designed to be used in a webhook event, which requires a Liveblocks Node.js client and a WebhookHandler. Check for the correct webhook event using isTextMentionNotificationEvent before running the function, such as in this Next.js route handler.

Returns
  • valueTextMentionNotificationEmailDataAsReact | null

    Returns text mention information, and a formatted React content ready for use in emails. Returns null if the text mention does not exist anymore or has been already been read.

    Unread text mention
    {  roomInfo: {    name: "my room name"    url: "https://my-room-url.io"  },  mention: {    id: "in_oiujhdg...",    roomId: "my-room-id",    createdAt: Date <Fri Dec 15 2023 14:15:22 GMT+0000 (Greenwich Mean Time)>,    userId: "user_0"
    // The formatted content, pass it to React `children` reactContent: { /* ... */}
    author: { id: "vincent@example.com", info: { /* Custom user info you have resolved */ } } },}
Arguments
  • clientLiveblocksRequired

    A Liveblocks Node.js client.

  • eventTextMentionNotificationEventRequired

    An object passed from a webhook event, specifically the TextMentionNotificationEvent. Learn more about setting this up.

  • optionsobject

    A number of options to customize the format of the content, adding user info, room info, and styles.

  • options.resolveUsers

    A function that resolves user information in Comments. Return an array of UserMeta["info"] objects in the same order they arrived. Works similarly to the resolver on the client. Learn more.

  • options.resolveRoomInfo

    A function that resolves room information. Return a RoomInfo object, as matching your types. Works similarly to the resolver on the client but for one room. Learn more.

  • options.componentsobject

    Pass different React components to customize the elements in the mention content. Three components can be passed to the object: Container, Text, and Mention. Learn more.

  • options.components.Container({ children: ReactNode }) => ReactNode

    The mention and its surrounding text container

  • options.components.Text({ children: ReactNode }) => ReactNode

    The text element.

  • options.components.Mention

    The mention element.

Resolving data

Similarly to on the client, you can resolve users and room info, making it easier to render your emails. For example, you can resolve a user’s ID into their name, and show their name in the email.

const emailData = await prepareTextMentionNotificationEmailAsReact(  liveblocks,  webhookEvent,  {    resolveUsers: async ({ userIds }) => {      const usersData = await (userIds);
return usersData.map((userData) => ({ name: userData.name, // "Nimesh" avatar: userData.avatar.src, // "https://..." })); }, resolveRoomInfo({ roomId }) { return { name: roomId, // "my-room-name" url: `https://example.com/${roomId}`, }; }, });
// { mention: { ... }, ... }console.log(emailData);
// { name: "Nimesh", avatar: "https://..." }console.log(emailData.mention.author.info);
// { name: "my-room-name", url: "https://example.com/my-room-name" }console.log(emailData.roomInfo);

Customizing components

Each React component in the mention context can be replaced with a custom React component, if you wish to apply different styles. Three components are available: Container, Text, and Mention.

const emailData = await prepareThreadNotificationEmailAsReact(  liveblocks,  webhookEvent,  {    components: {      // `react-email` components are supported      Container: ({ children }) => <Section>{children}</Section>,
Text: ({ children }) => ( <Text className="text-sm text-black m-0 mb-4">{children}</Text> ),
// `user` is the optional data returned from `resolveUsers` Mention: ({ element, user }) => ( <span style={{ color: "red" }}>@{user?.name ?? element.id}</span> ), }, });
// { mention: { ... }, ... }console.log(emailData);
// The components are now used in this React contentconsole.log(emailData.mention.reactContent);``;

prepareTextMentionNotificationEmailAsHtml

Takes a text mention notification webhook event and returns an unread text mention with its surrounding text as an HTML string. It can also return null if the text mention does not exist anymore or has been already been read. You can also resolve user & room data and customize the styles.

import { prepareTextMentionNotificationEmailAsHtml } from "@liveblocks/emails";import { isTextMentionNotificationEvent } from "@liveblocks/node";
// Get `liveblocks` and `event` (see "Setup" section)// ...
if (isTextMentionNotificationEvent(event)) { const emailData = await prepareTextMentionNotificationEmailAsHtml( liveblocks, event );
const email = ( <div> <div> @{emailData.mention.author.id} at {emailData.mention.createdAt} </div> <div>{emailData.mention.htmlContent}</div> </div> );}
// Send your email// ...

It’s designed to be used in a webhook event, which requires a Liveblocks Node.js client and a WebhookHandler. Check for the correct webhook event using isTextMentionNotificationEvent before running the function, such as in this Next.js route handler.

Returns
  • valueTextMentionNotificationEmailDataAsHtml | null

    Returns text mention information, and formatted HTML content ready for use in emails. Returns null if the text mention does not exist anymore or has already been read.

    Unread text mention
    {  roomInfo: {    name: "my room name"    url: "https://my-room-url.io"  },  mention: {    id: "in_oiujhdg...",    roomId: "my-room-id",    createdAt: Date <Fri Dec 15 2023 14:15:22 GMT+0000 (Greenwich Mean Time)>,    userId: "user_0"
    // The formatted content, as an HTML string htmlContent: { /* ... */}
    author: { id: "vincent@example.com", info: { /* Custom user info you have resolved */ } } },}
Arguments
  • clientLiveblocksRequired

    A Liveblocks Node.js client.

  • eventTextMentionNotificationEventRequired

    An object passed from a webhook event, specifically the TextMentionNotificationEvent. Learn more about setting this up.

  • optionsobject

    A number of options to customize the format of the content, adding user info, room info, and styles.

  • options.resolveUsers

    A function that resolves user information in Comments. Return an array of UserMeta["info"] objects in the same order they arrived. Works similarly to the resolver on the client. Learn more.

  • options.resolveRoomInfo

    A function that resolves room information. Return a RoomInfo object, as matching your types. Works similarly to the resolver on the client but for one room. Learn more.

  • options.stylesobject

    Pass CSS properties to style the different HTML elements in the mention content. Four elements can be styled: paragraph, code, strong, mention, and, link. Learn more.

  • options.styles.paragraphCSSProperties

    Inline styles to apply to the mention container container. It's a <div /> element under the hood.

  • options.styles.codeCSSProperties

    Inline styles to apply to the code element.

  • options.styles.strongCSSProperties

    Inline styles to apply to the strong element.

  • options.styles.mentionCSSProperties

    Inline styles to apply to the mention element.

  • options.styles.linkCSSProperties

    Inline styles to apply to the link element.

Resolving data

Similarly to on the client, you can resolve users and room info, making it easier to render your emails. For example, you can resolve a user’s ID into their name, and show their name in the email.

const emailData = await prepareTextMentionNotificationEmailAsHtml(  liveblocks,  webhookEvent,  {    resolveUsers: async ({ userIds }) => {      const usersData = await (userIds);
return usersData.map((userData) => ({ name: userData.name, // "Nimesh" avatar: userData.avatar.src, // "https://..." })); }, resolveRoomInfo({ roomId }) { return { name: roomId, // "my-room-name" url: `https://example.com/${roomId}`, }; }, });
// { mention: { ... }, ... }console.log(emailData);
// { name: "Nimesh", avatar: "https://..." }console.log(emailData.mention.author.info);
// { name: "my-room-name", url: "https://example.com/my-room-name" }console.log(emailData.roomInfo);

Styling elements

Each element in the comment body can be styled with custom CSS properties, if you would like to change the appearance. Five elements are available: paragraph, code, strong, mention, and link.

const emailData = await prepareTextMentionNotificationEmailAsHtml(  liveblocks,  webhookEvent,  {    styles: {      paragraph: { margin: "12px 0" },
mention: { fontWeight: "bold", color: "red", }, }, });
// { mention: { ... }, ... }console.log(emailData);
// The elements in the mention content are now styledconsole.log(emailData.mention.htmlContent);

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