Using Clerk with Liveblocks (without AI’s help…)
Although AI can help with a lot of things, if something is undocumented somewhere, it’s likely going to guess a bunch and be wrong.
When it came to using Clerk (our Auth platform of choice) with Liveblocks, there wasn’t anything I could locate online… so I had to dig in, roll my sleeves up, and do it the old fashioned way (read: trying and trying again).
How it started
We wanted to introduce the ability for our Stories and Posts to be interactive in some way.
I'd initially started playing with including Clerk auth in the project for two reasons: 1) as a way to learn about adding auth to a platform, and 2) so that we could introduce some features that sat behind auth protections.
Clerk is amazing if you've not had a chance to experiment with auth, it abstracts away so much of what makes auth routing complex and a pain to implement (especially using social providers and other login options).
When it came to thinking through what features to put behind this authentication (hint: there's some cool stuff for hiring managers coming!), Liveblocks came to mind.
With Liveblocks, we could introduce the ability to leave comments directly against a specific part of the article/story, and, have your first name and avatar there too. This felt like a nice way to start to bring in a social element and to bring a Figma-like experience to the wider Design Engineering community.
Introducing "Conversations"
With Conversations, Design Engineers can add extra context on top of the discussions we have in Stories, they can ask questions about the context if it's missing, or have a difference of opinion and spark a debate. In Posts they can provide links to useful resources or extend the conversation around a specific topic in a thread.
Conversations are the first step in bringing the community closer to the material and allowing Design Engineers of different levels to share, learn and engage in discourse directly with the materials we and our contributors produce.
How I Built This
Prerequisits
- This is not a tutorial
- You should already have a Next.js site with Clerk authentication working
- You should have a Liveblocks account with your API keys set up
So let's jump into the details of how this all works, and how I got Liveblocks and Clerk connected together.
It starts with the Liveblocks comments canvas example. From there you need to make some tweaks. This isn't a full tutorial on how to set it up, just a framing around the nuances to connect these two platforms.
1. Update your database.ts to fetch from Clerk
import { clerkClient } from "@clerk/nextjs"; export async function getUsers() { const users = await clerkClient.users.getUserList(); return users.map((user) => ({ id: user.id, email: user.emailAddresses[0].emailAddress, name: user.firstName, avatar: user.imageUrl, })); }
2. Update your liveblocks-auth route
In this case, we pull the currentUser
from the Clerk server package and utilise the id for the session.
import { Liveblocks } from "@liveblocks/node"; import { NextResponse } from "next/server"; import { currentUser } from "@clerk/nextjs"; /** * Authenticating your Liveblocks application * https://liveblocks.io/docs/authentication */ const liveblocks = new Liveblocks({ secret: process.env.LIVEBLOCKS_SECRET_KEY!, }); export async function POST() { if (!process.env.LIVEBLOCKS_SECRET_KEY) { return new NextResponse("Missing LIVEBLOCKS_SECRET_KEY", { status: 403 }); } // Get the current user's unique id from your database const user = await currentUser(); // Create a session for the current user (access token auth) const session = liveblocks.prepareSession(user ? user?.id : "anonymous", { userInfo: { name: user?.firstName || "Anonymous", }, }); // Use a naming pattern to allow access to rooms with a wildcard session.allow("*", session.FULL_ACCESS); // Authorize the user and return the result const { status, body } = await session.authorize(); return new NextResponse(body, { status }); }
3. Users and Search routes
After this, we want to make sure our Users / Search routes are working nicely by importing the getUsers
function from our database.ts
file.
import { NextRequest, NextResponse } from "next/server"; import { getUsers } from "@/lib/database"; /** * Get users' info from their ID * For `resolveUsers` in liveblocks.config.ts */ export async function GET(request: NextRequest) { const users = await getUsers(); const userIds = new URLSearchParams(new URL(request.url).search).getAll( "userIds", ); if (!users) { return new NextResponse("Missing or invalid userIds", { status: 400 }); } return NextResponse.json( userIds.map((userId) => users.find((u) => u.id === userId)), ); }
import { getUsers } from "@/lib/database"; import { NextRequest, NextResponse } from "next/server"; /** * Returns a list of user IDs from a partial search input * For `resolveMentionSuggestions` in liveblocks.config.ts */ export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url); const text = searchParams.get("text"); const fetchUsers = await getUsers(); const filteredUserIds = fetchUsers .filter((user) => text ? user?.name?.toLowerCase() === text.toLowerCase() : true, ) .map((user) => user.id); return NextResponse.json(filteredUserIds); }
4. Getting the current Clerk user To be able to show the current user's avatar when pinning and leaving a comment (as well as rendering the user list when creating a comment/thread), we have to get the user via Clerk's client API.
import { useUser } from "@clerk/nextjs"; // const createThread = useCreateThread(); const creator = useUser(); // <div className={`${avatarStyles.avatar}`} style={gradientStyle}> {creator && creator.user?.imageUrl ? ( <Image src={creator.user?.imageUrl} alt={creator.user?.firstName ?? ""} width="28px" height="28px" draggable={false} /> ) : ( <div /> )} </div>
5. Wrapping with a CommentsPresence Finally, this meant I could wrap the CommentsPresence around the page(s) that it required and then pass in the id of the content from the URL params.
{ // return ( <CommentsPresence roomId={params.slug}> <div> // </div> </CommentsPresence> ); }
And that's that, once you've hooked up all the other logic you need, you'll be fetching the signed in user from Clerk and passing it to your Liveblocks session with its id
property and be able to associate comments with the right people. You'll also see your Clerk users in the list of available people to @mention in the comments/threads.
Sign Up today
With these updates, we're excited to enable the ability to sign up and start leaving comments today.