So now let's go ahead and let's connect to Liveblocks. So use the link in the description or go to liveblocks.io and go ahead and create an account. Once you are inside you're gonna have two default projects, the development and production. So for this one I'm gonna click on development right here and then inside of my API keys right here section I can see my public key which I'm going to need to create a LiveBlocks client. So now let's go ahead and let's go inside of our terminal so we can set up Liveblocks properly.
So for this step I'm gonna shut down all of my terminals. I don't want anything running because I will just be installing Liveblocks. So let's go ahead and run npm install at liveblocks slash client and after that let's go ahead and install liveblocks slash react so let's wait a second for this to finish and then liveblocks slash react. After this has been installed we're gonna be able to run an npx command to initialize the liveblocks.config.ts file. So for that run npx create liveblocks-app at latest and then let's write dash dash init dash dash framework and then react so let me zoom out and try and show you this in one line so npx create live blocks app at latest ____init ____framework react all right and let's run enter and that should set up our live blocks config so just confirm the newest version if you get this prompt.
Would you like to use our React Suspense hooks? You can select yes for that. Are you using TypeScript? Yes for that as well. And there we go.
Liveblocks.config.ts has been generated. So now you should be able to find this file right here liveblocks.config.ts and as you can see right here we have an error because we have TypeScript added here and it's expecting an actual public API key. So let's go ahead for now and remove the Auth endpoint. Let's remove the throttle and let's just uncomment the public API key. And then we can use this one from our API key settings here.
So just click to copy this. And I'm going to paste it here like that. So it should be in one line of course like this. There we go, great. And now let's go ahead and let's create a reusable room component which will wrap our existing canvas.
So for that I'm going to go inside of components here and I'm going to create a file room.tsx let's mark this as use client and let's go ahead and import react node from react. Let's import room provider from at slash live blocks dot config. And let's go ahead and import client side suspense from live blocks react. I'm just going to keep this to separate because this is our alias path to a file, right, which we have right here, LiveBlocks.config and in here you can see everything that is exporting like a room provider, which we are importing here. And this is an NPM package.
So that's why I like to separate this imports because this is an alias to my file and this is an NPM package. And now let's just export const room. Let's go ahead and give it a type of children to be react node children like this and then in here very simply we're going to return a room provider and in here we're not just going to accept the children we are also going to accept an actual room id so let's just go ahead and add room id prop as well and define it here to be a string and then we can pass in the ID to be a room ID right here and initial presence is just going to be an empty object for now and then inside we're going to add client side suspense and we're simply going to go ahead and render the children. And let's add a fallback here for now to just be a div. Loading.
Like that. Perfect. So I just want to be consistent with the way I'm creating props so I'm going to separate this like I always do. Room props like that. And then I will simply use room props here.
You don't have to do it. I just like to be consistent with my types. And now let's go ahead back inside of app board, board ID, page.tsx right here. And now we're gonna go ahead and import room, not from Liveblocks client, but from components room like this. And now we can wrap our canvas in a room just like this And let's go ahead and pass in room ID to be params board ID, like that.
So now, if you take a look in your live kit here, rooms, I'm not sure if we're gonna have any rooms. There we go. We have no rooms yet. But I think that just by refreshing this page after we run npm run dev, the fact that we wrapped our canvas in a room component, that is going to trigger a new room. So let's do npm run dev.
Let's open a new terminal for npx-convex-dev. Like that. And let's go directly to localhost 3000 first so we ensure that our authentication and everything is working. So I'm just going to refresh one more time to make sure this is synchronized. There we go.
So once I click here and once I refresh here, there we go. You can see how I have a new room ID which has the same ID as my board ID from convex. So this way we synchronize the live blocks with our convex database in that way by using the same unique ID. So we know which live blocks room relates to which canvas board. Perfect.
So if you go ahead and click on some other boards that you have, that's gonna go ahead and create a new LiveBlocks room for you. So right now I only have one room, but if I go to some other organization right here, There we go. Now I have two rooms and it uses the ID of that board. You can see that the ID is the same as here. Perfect.
So now that we have that, I want to go ahead and extend this room component a bit. Since it's reusable, I want to make sure that the fallback that I'm passing is also reusable. So this is what I'm gonna do. I'm gonna go ahead and add fallback here and I'm gonna attempt to use this non-nullable React node or null like that and then I'm gonna extract the fallback here and I'm gonna pass the fallback like this. No errors here but now my page should have an error and in here instead now I can pass a div for example loading like that and now if I refresh here this should still work just fine.
Great! What I want to do now is I want to create an actual loading for my board. So inside of here I'm gonna go ahead and create a canvas loading element. So let's go ahead and create canvas loading.tsx or maybe we can just name it loading. Let's name it loading.
And basically what we're gonna do here is we're going to import skeleton from components UI skeleton and we are going to import loader from Lucid React. And then let's go ahead and export const loading and let's return a main element and I want it to be the same as my canvas right so we can copy this specifically like this and let's see if we have to change anything. So we need relative, we need BG neutral but let's also do this. Let's add flex items center and justify center. So the reason I'm adding this is because I'm gonna have a loader Which I've imported in the middle of the screen.
So class name height 6 width 6 text muted foreground and animate spin let me just see if I can already use this. So inside of my page.dsx, I'm gonna remove this and use loading from .slash components loading. And I think that If you have a slow connection, you might see a little loader for a second. The thing is that there's nothing loading from Liveblocks at the moment. So perhaps we won't even able, we are not even able to see it right now.
So here's what I want to do. Let's just manually return a loading. Just so we can see what we're building there we go so now we can see our loading element here so I want to create that room loading component first and let's go ahead and continue developing it So now I want to create like little skeletons or placeholders for all of those floating elements Which we're gonna have which are the info here the participants and the toolbar here so let's go ahead and Let me open up the info component So what we can technically do is write info.skeleton to be function info.skeleton And then we can just return this, right? And it can actually be a self-closing tag, I believe, because it doesn't matter what's inside. It's going to be empty either way.
And you can use the skeleton component if you want to. Let's actually use the skeleton component. Yeah. So I'm going to expand this. My bad.
Let's use this skeleton inside from components UI skeleton like this so we are actually going to import that here in the info let's give the skeleton a class name of h full with full and BG muted 400 like that And let's go ahead and give the outer div some changes in the class name. So we are gonna have to manually give it a specific width. So let's go ahead and do that. Let's give it a width of fixed 300 pixels like that. And let's see how this looks for a skeleton.
So if I go back to my loading and below the loader, if I add info from .slash info.skeleton, and I can remove this skeleton from my loading. There we go. I think this looks okay, right? Because if you add, you can change the skeleton to be more visible. For example, you can remove this BG muted, but then it just looks weird because the background itself is already dark.
So I think you can just increase this and then it looks better because it's just a blank space indicating that something is loading here and we have this loader here. So I think it's kind of clear that it's loading that information above. And the same way we created this info skeleton here we're now going to create the participants skeleton as well so let's write participants.skeleton is going to be function participants skeleton and we're going to go ahead and copy and paste this like this And let's just add a skeleton component from components UI skeleton. Let's give it a class name, AgeFull and width full and BG muted 400. And let's give this one a fixed width of, let's do, I don't know, 100 pixels.
Like that. And let's add a skeleton component inside from components UI skeleton so I just copied this outer div right and instead of creating you know so in this toolbar we have like the outer div which just positions the elements and then inside we have this bg-white So how about I combine the two. So what I can do right here is just add BG white and then I can also fix the height of this to be 360 pixels because that's how much elements I expect inside later. And let's also fix the width to be 52 pixels. Like that.
And let's give this a class name, height full, width full and BG muted 400. Oh, BG muted 400 doesn't exist at all. That's why it looks white. Okay, so we're gonna refactor this. Let's just take a look at how this looks now.
So, toolbar.skeleton So make sure you have info toolbar and skeleton and let's refresh this. Okay, so this looks good. This looks good. And the toolbar is missing a shadow. So let's go inside of the toolbar here and just add a shadow MD.
Okay, and we also need a rounded MD, I believe. There we go. Now it looks just like the others. And I think we actually don't need the skeleton inside. So if I remove the skeleton, yeah, nothing has changed.
So we don't need a skeleton and all of this can be self-closing divs. So I just fixed that in the toolbar skeleton. So we don't need nothing inside. We can do a self-closing tag like this and you can remove the unnecessary skeleton. Now I'm going to go inside of info and do the same thing.
We don't need the skeleton import and we don't need this inside. We don't need the ending tag because this can just be a self-closing tag. And let me just refresh to confirm this is still working. This is still working. Also, yeah, when you're modifying code inside of this info skeleton like this.
Don't rely on hot reload too much. Manually reload to see your changes because I think that if I just change this to BG-RED-500. Okay, now it works. Of course, when I try to demonstrate it, it works. But while I was developing, hot Reload gets kind of confused with this kind of experts.
So if you're not seeing your changes immediately, just refresh manually. All right, so we fixed that for the info. We didn't need that skeleton component. And the same thing is true for the participants component right here. We don't need the skeleton import, we don't need to use it here and we don't need...
We can just use the self-closing tag like this. There we go. So now we have a nice little skeleton page, our suspense for actually loading the canvas room connected to live blocks. So why are we even going to need the loading for that? We are going to need the loading because we're gonna in this room component later so we can now remove this return.
We can now just let it go back to it's going to use that loading as a fallback we've now know that it works we know how it looks like otherwise it would have been very hard to develop it. So the reason the loading is later going to be needed is because inside of our liveblocks.config.ts in the next chapter we're gonna go ahead and modify this create client to also have an API endpoint which will authenticate the user which is allowed or not allowed to visit this board. So we successfully connected to our live blocks and we are now ready to do that. Great, great job!