Sign in

Get started with AI Presence using Liveblocks and Next.js

Liveblocks is a realtime collaboration infrastructure for building performant collaborative experiences. Follow the following steps to show an AI agent as a real user inside your Next.js /app directory application: in the avatar stack, and as a highlighted box around the form field or cell it’s currently editing, using setPresence from @liveblocks/node and the useOthers hook from @liveblocks/react.

Quickstart

  1. Have a Presence app ready

    To add AI Presence on top of your existing app, you first need a Liveblocks Presence setup with AvatarStack and authenticated users. Open up your app, or set up presence if you haven’t already.

    Get started with Presence

  2. Install @liveblocks/node

    We’ll set the AI agent’s presence from the server, so install @liveblocks/node.

    Terminal
    npm install @liveblocks/node
  3. Add your secret key

    Create a .env.local file and add your Liveblocks secret key from the dashboard.

    .env.local
    LIVEBLOCKS_SECRET_KEY=""
  4. Add an AI user to your database

    The AI agent appears in the room like any other user, so it needs an ID, name, and avatar. Add a dedicated user for your agent alongside your real users, and return it from resolveUsers so it can be rendered in AvatarStack.

    Find where you fetch data in your resolver functions…
    <LiveblocksProvider  authEndpoint="/api/liveblocks-auth"  resolveUsers={async ({ userIds }) => {    const users = await fetchUsers(userIds);    // ...  }}>  {children}</LiveblocksProvider>
    …and modify the return values to include an AI user
    export const AI_USER_INFO = {  id: "__AI_AGENT",  info: {    name: "AI Assistant",    avatar: "https://liveblocks.io/api/avatar?u=__AI_AGENT&agent=true",    color: "#6366f1",  },};
    async function fetchUsers(userIds: string[]) { const users = [];
    for (const userId of userIds) { if (userId === AI_USER_INFO.id) { users.push(AI_USER_INFO); continue; }
    const user = await (userId); users.push(user ? { id: userId, info: user.info } : undefined); }
    return users;}
  5. Show the AI in your avatar stack

    To make the AI appear in AvatarStack and useOthers from the server, call setPresence. Use this to signify your agent is running a task in the room.

    app/agent-presence.ts
    "use server";
    import { Liveblocks } from "@liveblocks/node";import { AI_USER_INFO } from "./database";
    const liveblocks = new Liveblocks({ secret: process.env.LIVEBLOCKS_SECRET_KEY!,});
    export async function runAgentTask(roomId: string) { await liveblocks.setPresence(roomId, { userId: AI_USER_INFO.id, userInfo: AI_USER_INFO.info, data: {}, ttl: 30, // How many seconds it should appear });
    // Run your AI task or workflow // ...}

    The agent will now appear in presence alongside real users.

  6. Show AI presence in custom UI

    With useOthers you can create custom AI presence within any UI. An example of this is showing a highlighted box around the part of the interface that the AI is editing.

    To set this up, first set presence types in your app. An editingId string can represent an element that the AI is currently editing, such as a form field, and a value null can signify that the AI is not editing anything.

    liveblocks.config.ts
    declare global {  interface Liveblocks {    Presence: {      editingId: string | null;    };  }}
    export {};

    Next, when using setPresence on the server, set the editingId property to the ID of the element that the AI is currently editing.

    app/agent-presence.ts
    "use server";
    import { Liveblocks } from "@liveblocks/node";import { AI_USER_INFO } from "./database";
    const liveblocks = new Liveblocks({ secret: process.env.LIVEBLOCKS_SECRET_KEY!,});
    export async function runAgentTask(roomId: string) { await liveblocks.setPresence(roomId, { userId: AI_USER_INFO.id, userInfo: AI_USER_INFO.info, data: { editingId: "title-1", }, ttl: 30, });
    // Edit the input field with AI // ...}

    On the client, use useOthers to create a small component that draws a highlighted box around the targeted element when the AI is editing it.

    app/AiPresenceEditFrame.tsx
    "use client";
    import { useOthers, shallow } from "@liveblocks/react";import { AI_USER_INFO } from "./database";import { shallow } from "@liveblocks/react";
    export function AiPresenceEditFrame({ editingId }: { editingId: string }) { // Check if the AI user is online AND editing the current ID const aiIsEditing = useOthers(((others) => { const ai = others.find((o) => o.id === AI_USER_INFO.id); return ai && ai.presence.editingId === editingId; }), shallow);
    if (!aiIsEditing) { return children; }
    return ( <div className="relative"> <div className="rounded-sm rounded-tl-none ring-2 ring-indigo-500 ring-offset-1 ring-offset-white"> {children} </div> </div> );}

    You can then wrap any part of your your UI with AiPresenceEditFrame, give each one a unique editingId, and if the AI is editing that ID, a highlighted box will appear around it.

    app/Form.tsx
    "use client";
    import { AiPresenceEditFrame } from "./AiPresenceEditFrame";
    export function Form() { return ( <form> <AiPresenceEditFrame editingId="title-1"> <input /> </AiPresenceEditFrame> <AiPresenceEditFrame editingId="description-1"> <textarea /> </AiPresenceEditFrame> </form> );}
  7. Hide the AI when it’s done

    You can optionally choose to hide the AI's presence when it's completed editing. To do this, set the minimum ttl value to 2 seconds.

    await liveblocks.setPresence(roomId, {  userId: AI_USER_INFO.id,  userInfo: AI_USER_INFO.info,  data: { editingId: null },  ttl: 2,});

    You have AI presence set up! In some apps, it may be useful to use multiple different IDs for AI, as this way you can individually control when each AI appears and disappears.

What to read next

Congratulations! You’ve set up the foundation for AI Presence in your Next.js application.


Examples using AI Presence