ever wondered how to build figma a collaborative design tool with real world and realtime features like cursors chat and common bubbles well today's your chance to learn and build all of these features with me hi there and welcome to a brand new project-based video that isn't just another CR up today we'll build and deploy a figma clone with live collaboration features like multic cursors cursor chat and reactions active us users common bubbles creating different shapes uploading images modifying properties to any value free form drawing undo redo checking history deleting scaling moving clearing exporting canvas and much more that's a lot right but the most important part is that all of this works in real time and the sessions are stored which means that anything you do here will also appear to whoever else is in the same file we'll build this cool application using everyone's favorite next4 in Tailwind CSS before we start coding let me show you how the app works up in the top right you'll see active users who's here and what they're doing with their cursor unlike typical online whiteboards like Meo and fig Jam where you can create fixed specific shapes on click and then modify the size by dragging it or change fewer to non properties like color dimensions and stroke without any proper user collaboration our app is different just like figma we do all this including creating different shapes like rectangles circles and triangles and even adding text and uploading images with custom sizes by dragging your cursor with complete customization over color Dimensions export and for the artist in you there's a free drawing mode where you can let your creativity flow you might notice something on the left side every time I create an element it pops up in the left sidebar that's our realtime history or layers watch as I click this icon to clear the canvas and create something new to see how they Stack Up based on the shapes we create pretty cool right now check this out if I open up another browser Every Move I Make creating moving or deleting instantly shows on the right side and yes we've got the custom rightclick menu and keyboard shortcuts too like contrl C and V for copy pasting and contrl z and y to undo redo just like in our everyday developer routine now if you want to collaborate with someone in real time I can press forward slash or right click to chat with them and say hi to add some reactions I can press the letter e or open the rightclick menu and then click anywhere on the canvas and everyone will know how I feel about their design to make this application packed with even more useful features we can use the comments icon inside the Navar to add a comment bubble similar to figma for suggestions or to leave a happy note about the design I can reply and even send emojis if everything looks good I can click on the right icon to mark this thread as complete exciting isn't it I'm sure I heard you say yes all of this is possible thanks to live blocks The Talk of the Town bringing collaborative experiences to your apps in days and not months it's so user friendly that even for sale utilize their features in their ship live stream in this video I'll teach you how to integrate live collaboration not just into this figma clone project but into any app you'll ever build you don't have to learn any special framework or anything but you will add an exciting technology to your skill resume now you may wonder about the prerequisites for this tutorial all you need is a good grasp of JavaScript and rea act and if you don't have it yet don't fret check out the crash courses on YouTube to get up to speed finally if you want to dive deep into NEX js14 and really understand how the internet works while also learning by far the most used react framework learning All The Cutting Edge features like server actions routing and serers side rendering but also how to properly manage state in nextjs then check out our ultimate nextjs course which I'll link in the description 3,000 developers have already joined and they love it so if you're ready to get started let's dive right into the code to get started building our one-of-a-kind figma clone you can create a new empty folder on your desktop and drag and drop it into your empty Visual Studio code window that should leave you with something like this where you can open up the terminal and we are ready to get started and of course we'll start by using the none other than the react framework for the web nextjs so go to next js. org and copy the installation command once you're back simply paste it and add the slash at the end to initialize the repository right within our current folder it'll prompt you to install the next GS CLI to which you can say why of course and then it's going to ask you a couple of questions to set our project up first of all are we going to use tabs script for this project the answer is definitely yes and you don't need to have any prerequisites of typescript to be able to follow along I'm going to teach you how to approach it from scratch this time we're going to pair typescript with eslint to follow all the best practices of building modern and scalable web applications so say yes to that as well Tailwind of course of course we're going to say yes as it almost became the default way of styling our nextjs applications we don't need to use the source directory but we will use the app router and we don't need to customize the default import Alias and right away the next GS CLI will set up our starter nextjs application so we can dive right into the development there we go everything has been installed successfully so if we open up our file explorer you can see that we have the app folder inside of which we have the layout and the page now since we're building a figma clone we need a canvas on top of which we can do all the work and there is a package called fabricjs which is an entire framework that makes it easy to work with the HTML 5 canvas elements it is essentially an interactive object model on top of canvas elements it is designed for HTML 5 canvis manipulation allowing you to create and manipulate a lot of interactive graphical content on the website today we'll explore one of the best fabricjs use cases which is implementing Custom Design tool in this case that's our figma clone so we can write mpm install and get started with installing our first package or dependency called fabric alongside fabric will also install a package called uuid which is just a package that allows to create unique IDs which we'll need many of because each one of our elements in figma has to have its own unique ID but before we go ahead and install this packages let me tell you a bit more about the second most important package which we'll use to make the building of our figma clone possible while fabric will allow us to create a canvas and manipulate elements on it a package called live blocks will enable you to implement real-time collaboration features into any app You're Building as soon as you scroll down you can immediately see what building blocks they give you for enabling collaboration within your apps you have things like presents where you can see what people are doing broadcasting real-time events adding comments for discussions on the website even sharing in permission documents and more and the best thing is it integrates directly into our next GS ecosystem and the best of all is in this single video while you're building your own figma clone you'll learn how to implement most of fly Block's features such as the Avatar stack cursors comments and even our entire whiteboard feature built from scratch so exciting stuff ahead and best of all is that it's a developer Centric tool which allows you to really easily use it within your codebase and sync your application with other users so to get started with using it click the special link down in the description that will allow you to follow along and see exactly what I see then go to sign up sign up with GitHub or Google this will redirect you to your dashboard where you can see some starting information in this case I closed it because I'm going to teach you everything and we can get started with creating a new project let's name our project figma clone and we can choose the development environment for now immediately you'll be able to track all of your users and some new items will appear on the left such as rooms here you'll be able to create rooms that people can use within your application as well as schemas which allow you to Define what kind of data you'll be storing in live blocks and the next thing we have to do is go to API keys and just copy the public key this allows us to go back within our app and immediately create a new file file called env. looc where we can store our nextore Public Live blocks corpore key there we go and then simply paste it right here now we can proceed with the installation process live blocks has super slick docs that allow you to get started off of an example by choosing what kind of collaborative features you want to include within your app such as whiteboard comments and more in this case of course we're going to begin with a custom multiplayer experience you can even choose a framework and you can get started right away so let's copy these two additional packages which we need to install and paste them within our Command so now we have mpm install fabric uid at liveblock client and at live blocks for/ react I think that's going to be enough for now so let's go ahead and press enter the packages have installed and we can proceed to move forward we can just follow the steps from the docs so here we need to initialize the live blocks config TS file so let's copy this command go back to our code and just paste it right here it's going to ask us if we want to install the create liveblock CLI so let's say yes and it's going to ask us a couple of questions in this case we do want to use react suspense hooks we are using typescript and and that's it a life blocks config TS file has been initialized and we can see it right here it's asking us to add a couple of things such as our public API key so we can do that first for now we can remove everything from the create client besides the public API key uncommented and set it to nextore Public uncore Live blocks undoru uncore key and of course this is coming from process. EnV now just to satisfy tab script we can add an exclamation mark at the end because it doesn't yet know if it's actually there or not and with this we have successfully created a liveblock client that now allows us to use it within a live blocks room live blocks uses the concept of rooms separating virtual spaces where people collaborate and to create a real-time experience multiple users must must be connected to the same room when using next GS we recommend creating a room in a room. TSX file with the same directory as your current route so that is the app room TSX let's copy this entire file go back to code and create a new file within our app folder called room. TSX and paste what we copied from the docs now here it's trying to find live blocks config but I do believe we have to say dot do slash to get to the correct path and there we have it we have our first room next we have to use this room within our page so we can use it within our homepage which is just app page DSX so let's copy this block of code now go to page TSX within the app folder and override everything that we currently have in there we're simply importing a room from that/ room and we're importing this collaborative app from the collaborative app but the question is what is the collaborative app and it's essentially any feature that uses collaborative functionalities so in this case we can use the live blocks hook that figures out if there are any other users with us in a room right now called use others so let's copy it and then create a new file called collaborative app I'm just going to copy the title to ensure I spelled it correctly go here and create it within the app finally copy the code and paste it right here once again we're going to modify the config to come from the root of the directory so now within the page we are referring to our collaborative app which is coming from the same directory so I believe it should get it correctly if I say/ collaborative app. TSX and if I start typing TSX and remove it looks like now intellisense figures out that we actually do have a component there and it no longer complaints so now let's see what is the next step if you look at the next steps you can see that by default live blocks is configured to work without an authentication endpoint where everyone automatically has access to room we're going to use this approach for prototyping and our entire figma clone so with that said we have now set up the foundation to start our collaborative figma experience so going back to our app the only thing we have to do is clear our terminal and run m mpm runev to for the first time ever start our application on Local Host 3000 hold control or command and click the link you'll see a quick loading and then immediately after a piece of text that says there are zero other users online that's because only we are online but what happens if I split this View and try to open up Local Host 3000 in another tab once I do that you can see that there is one other user online as well and if I try it once again you'll see that now there are two users three users and so on you get the point our live functionalities are working so now is the time that we start developing the primary features of our figma application and then continue building collaborative functionalities on top of them so I closed all the other tabs so that we only have one and that means that we can get started with developing our application but before before we proceed there's just a tiny bit of setup that we have to do to make our further development experience much easier so one thing that we definitely have to do is install and set up shaten which is going to be our UI Library you can visit ui. shen.com and click get started then navigate to installation and choose nextjs here it's going to give you step-by-step instruction and we can skip step one because we have already initialized our nextjs application so we just have to run the shaten UI CLI to set up our project so let's copy it open up a new terminal and then paste it right here MPX shaten UI add latest in it we can choose the style we'd like to use here and we're going to go with default we can use slate we will use CSS variables and just like that the majority of the project set set up is automatically done now alongside chaten we also have Tailwind CSS installed here and tailin CSS has its own specific config where shaten overr the theme to provide some specific colors in this case we want to override that config to our custom figma theme so in the read me down below you can find and copy the full tailb config file and then simply override this one right here alongside with the tailin config we'll also have to update our global. CSS file so in the same read me copy the code and paste it right here you'll hear me mention the read me file many times throughout this video and that's because I want us to focus on what matters implementing the canvas and collaboration functionalities rather than focusing on implementing CSS or styling I want to teach you how to do the stuff that truly matters so now let's go back to our app let's remove this collaborative app experience because we just use that to test it out go back to our pH DSX and here within our room let's simply render an H1 and we can give it a class name of something like font DXL and just before we check out our app let me show you one thing and that is that within the globals we're using some special styles from live blocks and we have to install that to be able to use it so simply copy this part run mpm install and then install at live blocks react comments we're going to use it later on to implement the live comments functionality but for now we're going to Simply use their Styles once that is installed we can go back to our Local Host 3000 and there we go we have our huge title meaning that shaten and Tailwind CSS works as well and while we're doing this setup why don't we focus on fixing up the favicon which is this little icon that you can see to the left of your tab and the title as well nextjs makes it so easy so let's go to our file explorer and go to layout. DSX inside of here we're exporting the metadata so we can give it a title of figma Clone and we can give it a description of something like a minimalist figma clone using fabric.js and live blocks for Real Time collaboration there we go that's looking good keep in mind inside of here we're importing the global CSS and instead of using the inter font we can use the workor Sans font and we need to modify it right here by saying work sense is going to be equal to a call of workor sense and we can expand it a bit by using the Latin subset and also a variable of- Das font DW work- s so it's going to be activated once we use that class we can also get specific weights by saying weight is an array of 400 as well as 600 and we can also get a bold of 700 now scrolling down we can use this font right here within the body that's going to be work Sans that class name and we can also further style our class Name by modifying the Border Coler so let's make this a template string where we call the work Sans as a variable and then also provide a string of BG primary gray 200 and here's a key part we won't be using the room just within the page anymore we'll actually be using it within the layout so right here where we have the body we can wrap our children with the room so we can call it a room coming from that/ room where we put the children right inside it now we can go back to the page and we can remove the room from here as we're using it within the layout that's going to leave us with an empty slate where we have just an H1 and we can also change the color by saying text- White as now we have a dark background and before we check out our live website let's also modify the favicon and gather all the necessary assets will need throughout the build of this project and to get those assets go to the description of this video find the readme within it find the zipped assets folder download it unzip it and then simply drag and drop it to the root of our directory it's going to ask you whether you want to add it or copy it simply simp let say copy and you'll notice that now we have this new assets folder within our directory now the assets contains a couple of folders We already have such as the public as well as the lib folder so delete both of those folders from the root of our directory and then we can simply drag and drop all of these folders to the root of our directory constants we're going to repeat the process with our lib make sure to delete the old one first there we go lib as well we have public and finally we have types the last thing we have right here is going to be the favicon so delete the old one and then drag and drop the new one into the app folder and delete all of the assets that I provided now it might seem like there's a lot of files and folders right here but don't worry the only reason why I provided all of these to you is if either they are just some static code as you can see right here we're exporting some colors some shapes with their icons names and values nothing logic related all just simple values or if it's being picked from somewhere else like this hook right here use interval from danab brov's blog we just copy and pasted that or if I wanted to really document everything for you so in this case we have something like use max Z index which is a utility function and I went ahead and provid Ed thorough comments on exactly what this function does so we can explore it in more detail later on there's also this canvas file that does all the logic related to the canvas here I provided in-depth comments on literally everything that is happening and we're going to go through it later on once we consume some of the parts of this larger file so more in that soon but for now we have everything we need within our app we have our fabicon so let's check how our current APP looks like we have the same old title and dark background as before but now it says figma clone and we have the figma favicon right here which means that now we have everything we need to get started with developing it I'm going to put my browser side by side by our editor so we can see the changes that we make live there we go that's better while we're working on this I want to ensure that this text is nicely centered so I'm going to go back to app and then page and here we can nicely Center this H1 by putting it within a div and that div will have a class name equal to h-o w-o flex justify content of Center and items Center as well and to fully Center it I think we'll need to change this to height of 100 VH there we go and also give it a text Center as well alongside item Center and maybe we'll make it just a a tiny bit smaller like 2XL there we go that's looking great now before we implement the actual canvas and turn this into a real figma board first I want to teach you how to add live collaborative functionalities to any app and then we're going to upgrade them to match our figma and the first feature we'll start implementing is called live cursors it allows us to very quickly figure out if somebody else is in our app right now and what exactly they're doing so these two boxes present two different sessions within the same room and you can see the only thing you have to do is move your cursor and in real time in the other browsers you'll be able to see exactly what the other person does exactly like it is in real figma to make that happen first we have to create our live environment by going to or creating the components folder in the root of our directory and creating a new file called live . TSX here we can run rce which will immediately create just a basic react Arrow function component within here we can do a quick setup and by implementing the setup I simply mean calling those live cursor component which we also have to create so let's create a new folder within the components folder called cursor within the cursor we can create three additional files cursor TSX which is a singular cursor that you can see on the screen where you can also run rafc just to quickly get a started we can then create another component called live cursors TSX which is going to be a collection of all cursors showing up live on the screen and finally we can create something known as a cursor chat. TSX where you can also run rafc and we're going to see that in action very soon but for now let's focus just on consuming the live cursor within the live component our live component is going to be like a collection of all of the live functionalities we'll Implement live cursors cursor chat reactions and more so let's simply call the live cursors right here as a self-closing component and if we actually run our fce within live cursors we'll also be able to automatically import it I did that by pressing control or command space to give me this Auto Import possibility now we can figure out what live functionalities we need within live to then be able to pass it to live cursors and the first thing we need is something called others so we can see cons others is equal to use others coming from add live blogs config like this it is a simple hook and this hooks remember we used before in the demo example it Returns the list of all the other users in the room we can also quickly see what's happening within their docs where it says that it extracts data from the list of other users currently in the same room so it's not just showing the number of users it also extracts data from them and in this case we'll need the data about the positioning of their cursor so now we can pass others into live cursors as others as a simple prop you can see tab complaining already saying that it's not accepting it but now we can move into live cursors and accept it as our first prop of day others is equal to and we can say live cursor props coming from at types for/ type like so this is a predefined type that we have created before where I simply spend some time figuring out exactly which type it has to be in this case it is others which is a readon property of of user presence and base user metadata if you don't specify it I believe it will be specified either by tab script automatically if I didn't write it like here I believe it would have been specified by the liveblock steam itself because it's going to figure out the result is coming directly from this use others hook so now that we have the others the question is how to use them well first we can map over them and show a cursor for each new user so instead of returning something automatically here rather we're going to say others. map by mapping over every other person so we can say other and then for each one of these other people we're going to open up a new block like this instead of Simply getting other we can destructure some of the property so we can destructure the connection ID as well as presence which will give us some additional information such as their cursor position so now that we're mapping over them first we can check if there is no presence in which case we can simply return or exit out of the function by returning null if we do have the presence on the other hand we can return something else return a new cursor and this is the cursor component we have created not that long ago which looks like this so we simply call it cursor coming from that/ cursor and of course since we're mapping overhead we also need to provide it a key which is going to be a connection ID because one connection ID is paired with every single cursor or the user that joins our app now to this cursor we'll also have to pass some additional properties such as the color of that cursor and in this case I've made some predefined colors which you can get by saying colors all uppercased and you can import them from ad SL constants if you go into it you're going to notice that this is simply an array of a few colors then we can randomize it by accessing a specific property using the square brackets notation and then choosing a number with a connection ID and then using a modu operator and saying colors. length this is going to pick up a random color based off of the connection ID we can also give it the exposition by saying presence do cursor dox as well as y by saying presence. cursor .y and then also a message in case we are typing something by saying presence. message these are all the necessary properties that we need to pass to now actually show our cursor on this screen we need to know the position on the x-axis and the position on the y-axis to be able to cover this 2D plane so moving into our individual cursor we can now accept some props we can accept the color the X the Y and the message and say that that all of these are equal to type of props so at the top we can Define these props by saying type props is equal to and we can give it a color of a type string X of a type number y of a type number and now let's turn this text that just says cursor into an actual cursor we can do that by giving this div a class name equal to pointer-events dnone and if sometimes you're not sure what a specific Tailwind class does simply hover over it and you can see the full info and if this didn't show up for you just go to extensions search for Tailwind CSS intellisense and you should be able to install this package Tailwind CSS intelligence and then it will show up now next to this one we also want to give it a position absolute and top zero as well as left zero so first want to reset its position to be able to dynamically modify it using the style property so we can say style of transform and then we want to use the dynamic template string to call the translate X property and translated by X pixels to the xaxis like this and also provide the Translate Y property and we want to translate it by y pixels on the Y AIS now if we save it nothing's going to show up yet but we're working towards something and that something is just an SVG image that changes colors that acts like the cursor that you can see on the screen right now but it's going to be the cursor from the other people and I already gave you access to that cursor under assets so if you simply start typing cursor SVG you'll be able to see that it's coming from public assets cursor SVG and we can just self close it like this and provided a prop of color is equal to color now later on we're going to also implement the message part of this component where a cursor can actually display a message but for now we just need to be able to see it on the screen so now if we open up another browser and go to Local Host 3000 like I have done right here we hopefully should be able to see something happening but not yet nothing is happening I believe that's because we haven't even used or called the live component anywhere so of course it's not going to show but before that we have to resolve this typescript issue with the live cursors and the way we're calling them and the reason we're seeing this is because typescript is saving our ass if we go into this file there's one small mistake that we've made that is incredibly hard to notice and that is that this component is not actually returning anything if you think about about it we're just opening a function block right here and defining some kind of a map but we're never doing anything with that map so what we have to do is we have to return the output of the map for this component to work at all so now if we do that you can see that this is no longer complaining and finally we can go back to our original homepage which is within the app and then page and here right below the H1 we can call our live component which has the live cursors implemented so we can import it from components live now the reason we're having this error is because live blocks is having trouble creating the room context as context is a react and therefore browser feature we cannot call it in the server and if you know something about nextjs you know that by default all pages are service side rendered so since we'll be using live blocks as well as canvas most of these things will be client side rendered so we can just say use client right here at the Top If we save it you can see that now it goes away but now we have another issue saying that it cannot read properties of undefined reading x here referring to the cursor which means that the cursor is undefined which we definitely don't want so let's go to live live cursors and here alongside giving the check for the presence if the presence exists we also can can ensure that the cursor also exists by saying if no presence dot or rather question mark dot cursor that way we'll only show the cursor if the cursor information exists with that the error is gone and what we need to do before we can see the other cursor is update the presence of all of those cursors and we can do that by moving to the live. TSX file this is the file that will handle absolutely everything that has to do something with live blocks functionality so in this case we're going to use a second hook of the day const D structure and then that's going to be equal to use my presence and that's coming from live blocks config now this use my presence if you hover over it again this is very useful with a lot of different types of libraries and packages especially if they're well documented as soon as you hover over it you see what you need to pass to it you see what it returns back right here the presence of the current user of the current room in the function to updated and here you can update the X and Y AIS to know exactly where the cursor is so we definitely need to know where we are first we get my presence and update my presence like this and then from my presence we can just destructure the cursor information like so and then we can leave update my presence as it is and we can also Define the type for this one as any right here I promise we won't be using many NES throughout this video but this is just one of those situations now that we have our cursor and update my presence we can put it to use so we'll create three separate functions they'll be called const handle pointer move which is going to be equal to an arrow function this Arrow function will also be a call to the use callback hook like this coming directly from react so we can just wrap it in a used callback like so and then we can provide an empty dependency array the way that the used callback works of course if you import it from react is that it doesn't recreate this function every time it just takes this one instance of the function and then provides the outputs you can also hover over it to see more information it will return a memorized version of the Callback that only Chang changes if one of the inputs change so now this used callback function is going to accept an event so we can say event of a type react. pointer event and immediately we want to prevent the default Behavior so we can say event. prevent default and then we have to get the current cursor position we can do that by saying const X is equal to event. clientx minus event current target. get bounding client wct and we call it as a function that's supposed to be bounding right here and then we need to sayx so what the second part of the equation is doing is it's getting the width of the actual cursor so we subtract the exposition of the cursor to the actual position on the screen that's to get even more precise position and we can do absolutely the same same thing for the Y so const Y is equal to event. client y minus event. target. get bounding client direct doy after we have those we can call the update my presence coming from the hook that we have used before we pass in an object to which we pass the cursor and then we pass the X and Y values and this is the handle pointer move now I've told you that we're going to have two similar functions like this one and the second one is the handle pointer leave so we can duplicate this function below handle pointer move and rename it to handle pointer leave so this hides the cursor once we leave the screen in this case we don't need to get any calculations the only thing we have to do is update my presence to cursor is going to be set to null and then message is also going to be set to null that's going to be useful later on for once we Implement cursor chat and finally once we come back to the canvas we want to do something similar as what we do on the handle pointer move so we can duplicate handle pointer move one more time below and rename it to handle pointer down in this case we don't need event prevent default we get the positions and then we update my presence same thing as before now we need to make use of all of these three functions by adding the handlers on our div or rather the listeners so we can create a listener called on pointer move which is going to be equal to handle pointer move and you can guess it we need to do the same for the other two as well so that's going to be on pointer leave once we leave the screen as well as on pointer down once we come back to the screen so now our div is listening for all of these changes and once we enter with our cursor it should track the current cursor position and if we test it out it does appear like it's not tracking it but I can assure you it is but it's just that this div has to cover the entire screen and right now if we give it a class name and do something like a border five and Border green 500 so we can see where this div is you can see that it's nowhere to be found we cannot even see it and we thought that it's wrapping our entire thing so what we have to do instead is move these classes right here from our original div onto our new div that is within the live because we want to make our entire screen live so copy all the classes from here from page and move them over to the class name of live to this div right here of course without the class name right here there we go save it of course only after you fix the errors right here and also move this H1 from the page to the live because we want to ensure that it is within our live environment there we go so now it's in the center right here but I still cannot see my border maybe that's because I should have said border two there we go border 5 is not a valid tail in property so now you can see that we are wrapping our entire screen within this live environment within which we're tracking the position of the cursor so now let's remove this border because we know it's there fix the Styles and let's open up the second browser and would you look at that as soon as you hover over one screen right here the cursor moves nicely on the second as well this is great we can see exactly what the other person is doing on our screen or can we we can just see where they're moving but right now there's not a lot of functionality for us to communicate with them or for them to do anything on the screen at all we just have a single piece of text so the first thing we can do is make these two cursors across two different devices actually communicate and we'll do that using live Block's cursor chat the live cursor chat functionality will allow us to communicate with the other cursor you've been seeing on the screen if you press the forward slash a new cursor chat appears appears and as soon as you start typing like hi you can see that it appears instantly in real time on the other screen so let's go ahead and give some special powers to our cursor as well within our application we can move to the live cursors and then we can move to the cursor remember here we left some space for the message but not only that we have also already created a new component called cursor chat So within this cursor chat will be able to implement that input that you have seen that allows us to type in the message but before we do that we have to go back to our primary live component called live. TSX to First call. cursor chat so right here above the live cursor we can check if the cursor exists like this remember this is the same cursor coming from our hook Co right here at the top cursor coming from use my presence I'm going to also toggle down these three functions so it's easier to see and here we can call our cursor chat we can call it in a way as we would call a simple component self closing call to the cursor chat component and we can immediately import it we now need to pass it some information such as the cursor Itself by saying cursor is equal to cursor and we also need to pass it something known as a cursor state so right here at the top we can define a new State use State snippet like this is called cursor State and set cursor State at the start equal to an object that has the property of mode and here we can get the cursor mode coming from types type. hidden like this so first at the start we're setting the state or the mode of the state to cursor state hidden and this is simply coming from our types where we defined an enum which is just a possibility of a couple of different strings such as hidden chat reaction selector or reaction and these are all the different predefined values off which our state can be we also have to import Ed state from react and now we have this new state this will help us track the state of the cursor are we simply pointing do we have maybe a reaction select we'll see that soon or are we chatting and we can also update it within our handlers so on handle pointer leave we can remove this event prevent default as we don't need it and we can set cursor state to be equal to mode cursor. hidden because we're hiding it and this is the only thing that the pointer leave does and we don't need to modify the other two for now but now that we have this cursor State we can pass it to the cursor chat so we can say cursor state is equal to cursor State as well as set cursor state is equal to set cursor State and we can also pass in the update my presence is equal to update my presence now that they're passing all of these let's go into the cursor chat and let's make use of them here we can immediately and happily accept the information about the cursor as well as the cursor State and of course let's not forget about the set cursor State and up at my presence and that's going to be of a type cursor chat props coming from types great now let's start creating the layout of our cursor chat we're going to start with a div that's going to have a class name equal to Absolute because we need to match it with the position of the cursor top zero and left zero at the start but then using the style property as before we're going to modify the transform form properties of that div by giving it a template string of translate X and a value of cursor dox and move it by X pixels so we can do PX and also Translate Y which is going to be having a value of cursor doy and also specify the pixel value now we won't be able to see this yet in the browser but we will be able to see it soon as soon as we check for the cursor State we can say if cursor State mode is triple equal to cursor mode coming from types. chat so if we're chatting then display the following empty react fragment like this we can also wrap it in parentheses so it's easier to see where the new component begins there we go that is R now within this cursor chat we can also render the cursor SVG which we have rendered before and automatically imported and to it we can pass a color of # 0000 so that's a black color below that we need to create a div that's going to be used for our bubble form so we can create a div and that div will have a class name equal to position absolute left two top five BG blue of 500 padding X of four padding y of two text- smm for small leading Das relaxed which is going to modify the line height and text- white so we can actually see the color within that div and finally we can make it a bit rounded by giving it a rounded Dash in square brackets 20 pixels great now within it we need to figure out if there is a message in there so if there's a previous message we can check that like this cursor state. previous message and and then we're going to render a div and that div will simply render a previous message so here we can say cursor state. previous message so only if we have typed something before we want to show it before we show the input to enter a new message and this input will also have a couple of class names now we're not going to go ahead and build this entire thing without actually seeing what we're building right so for now we can put this cursor mode chat check right here all the way to the top and don't forget it's closing statement to like this we're going to comment that part out for now because this will allow us to actually see what we're creating as soon as you hover you'll be able to see our cursor chat appear right there great so now that we can see it let's proceed with styling this input we can give it a class name equal to Z10 to appear on top W off 60 for width border none BG transparent text- White placeholder dl-300 and as we do with many inputs outline of none if we do it now it's going to look much better we cannot even see that it is an input let's also give it an autofocus of true because as soon as it appears on the screen we want to start typing there we go that's already much better now we also want to modify its state we want to figure out how to modify the value so for that reason we can create two functions at the top within this function the first function will be called const handle change just a typical function that we do often in react that accepts e as an event which is a react. change event specifically of a type HTML 5 input element that's one function and the other function is const handle key down here we'll have to also get the event which is in this case of a type react. keyboard event of HTML 5 input element and here we'll have to monitor for the enter key in case we want to submit our message but now that we have the skeletons of these two functions we can pass them to our input we can say that the onchange value will be calling the handle change property like this or the function and the on key down we'll be calling the handle key down function looks like we misspelled it right here there we go we can also give it a placeholder where we can check if a previous message exists by saying cursor state do previous message if it exists then the placeholder will be an empty string else the placeholder will be type a message great finally every input needs to have a value which is going to be cursor state. message and we can give it a max length of about 50 now to test it out we can just hover over here and we can immediately see the great form that we have created but of course we have to put it to use so right now we're seeing it only on our screen and we're seeing it always but we only want to see it when its state is activated so soon enough we're going to bring back that if statement or theary operation that we had not that long ago but for now let's focus on updating our empty functions the handle change function has to update our presence by adding a message to it so we can say update my presence where message is equal to e. target. Val the value contains of course the value that we typed and also we have to modify the cursor State like like this set cursor State we call it and set it to an object where the mode of the cursor state is cursor mode. chat because we're chatting we can also reset the previous message by saying previous message is null and then provide a new message by saying e. target. value now we're handling the change and we also can handle the key down by setting the cursor state to to also be the mode of cursor chat so we can do it like that mode is cursor chat or cursor mode. chat and here we're going to set the previous message to be equal to cursor state. message so essentially we are setting what was previously the new message to the previous message and then we are resetting the message right here soon enough it will make sense why we're doing this but essentially it allows you to type something press enter and then get more real estate to type something else and of course we only want to create more screen real estate for typing once we press enter that's exactly how it is on real figma so we can say if e. key is triple equal to the enter key only then we do what we have done right now by moving this cursor state within that if and we can also have an LF where we can check if the e. key is escape in that case we can just set the cursor mode to Hidden there we go that's much better finally you can see that our typescript is complaining a lot here saying that we maybe don't have access to this cursor state so it's finally the time that we put this over within aary operator that we previously removed so let's take this line right here above this empty fragment so we can call it like this remove the comment indent it properly and then ended right here after the react fragment there we go so now it's no longer complaining because it knows that the cursor mode has to exist and it has to be chat so as you can see now we cannot really see anything so that means that we have to find a way to actually activate the cursor chat mode and if you remember in the demo that I've showed you that was just the forward slash key that would activate it but in this case it's not really doing it anything so let's go ahead and make it happen and to do that we can go back to our live component where we're doing literally everything regarding to the live and right here we need to add a use effect to keep track of our keyboard events that's going to listen to our forward slash key so we can open up a regular use effect with an empty dependency array and we can of course import it from react within this use effect we can create a new function con on key up which is once we press up the key which is again going to be equal to a keyboard event like this and also const on key down where we're going to also do the same thing keyboard event so here we want to figure out if the key is forward slash so what we can do on the on key up is say if e. key is triple equal to slash in that case we can set cursor state to be equal to an object where the mode is cursor mode. chat previous message is null and message is an empty string we can also modify the else if e. key is triple equal to escape like this in that case we can update my presence where we're going to reset the message to be equal to an empty the string let's not forget to also set the cursor state to be equal to mode of cursor mode. hidden and finally on key down we also want to have one if where if e. key is triple equal to forward slash like this then we want to call the e. prevent default to prevent the default browser Behavior then since we're using the update my presence we can add right here to our dependency array of the use effect and we can put our event listeners to use by saying window that add event listener key up is going to call the on key up key down is going to call the on key down and since we're being good react developers we also have to return to remove those event listeners like so so now if we save this react should be listening for our events and if we go go here and type forward slash you can see that type a message appears and we can actually start typing now I do think that I'm zoomed in a bit so it appears like a very big thing but there we go this is more like it now it's of the proper size and we can actually start typing the only question is will the other user be able to see this chat as well and if we try it out the answer is no it doesn't seem to be a appearing on the other cursor so why is that well let's go to our other cursor where we're rendering it which is going to be under live cursors and then cursor and would you look at that we gave ourselves a little to-do to do later on before and now is the time to actually implement it so now we actually have access to the message belonging to this cursor not just the cursor position as we had it before and we can check if message exists and in that case we can render a div and that div will render a ptag that's going to render the message so now if we save this and try it once again you can see a little test although it's very dark let me make it a bit lighter for you by giving this ptag a class name of text- white so now if we type test you can see it actually appears in real time on the right side but of course let's make it look a bit better by styling this div right here and giving it a class name equal to Absolute left of two to just move it a bit away from where we are top of five that's going to look like this rounded -3 XL padding X of four and padding y of two let's also modify the background color to be the color of the other users cursor that's exciting so we can give it a style property where we modify the background color to be equal to the color we have from their cursor and we can style the P tag by giving it a white space like this white space all one word no WAP and text- smm and leading Das relaxed that's going to change the line height now if we do this and save if we try typing something you can see that it appears within the bubble of the same color of the cursor so we can say hi there I'm building figma and then they're saying the same thing or maybe the other person can now go here and say hi I am building it too and you can see it appears in the red color this is wonderful so now we're not only tracking the position of the other person that's currently within our app we are also able to communicate with them and all of this is amazing immediately done by using the live cursor chat functionality but we're going to take it a step further and also Implement reactions reactions are a really cool thing that allows you to press the letter e and then immediately express your thoughts maybe you're looking at a very well-designed design that allows you to very easily create your website in which case you would do something like this that looks great or maybe somebody created a 3D Madness that's so hard to develop in which case you would just Spam sad faces because it would be hard to develop in any case you can also Escape that emoji or say this is good because now we have the chat functionality too but with that said let's go ahead and Implement reactions to start implementing reactions we can for now close all of the files we currently have opened I usually do that by holding command and then pressing w or you can just close them manually we can then go to our components folder and create a new folder within components called reaction within reaction you can create a new file called reaction button. TSX and run ource also within reaction called Flying reaction. TSX and there we can also run RFC let's go ahead and get started with the reaction button this reaction button is exactly what you're seeing right here a selector in a sense that allows us to choose which kind of emoji or reaction we want to take so we're going to have a button for each one of these selections this reaction button and flying reactions are all already provided to us by live blocks in their example so let's take first the reaction selector and you can see here essentially it's just a selection of reaction buttons so what we can do is copy their entire example and I'm basically just at the live cursor chat example on the live blogs documentation you can easily find it just by Googling live cursor chat example then simply copy it and in our case we can paste it right within the reaction button component by overwriting everything and then pasting what we have this brought in both the reaction Button as well as the reaction selector which we're experting from this file and let's also not forget about the flying reaction which we can get from here which we can completely copy go back to the code and override our flying reaction too one thing that we must not forget is that there's also a flying reaction module CSS which you can also copy go back back to our code and create a new file called index. module. CSS and paste that file that we copied these are just some additional animations for our emojis and since we called it index module we can go here and just modify the name to index. module. CSS now let's put our reaction selectors as well as our flying reactions to the test can you guess where we'll be doing that it's going to be where we use all of the live functionalities within our live. DSX component so right here below calling the cursor we can check if the cursor State mode is triple equal to cursor mode. reaction selector if it is then we want to render our reaction selector component automatically imported from the reaction button and of course we also have to pass the set function to it which we have to Define at the top so let's go all the way up right here and Define a new state by using the US state snippet which is going to be called reactions also set reactions at the start equal to an empty array and we can further Define the type by saying this is of a type reaction coming from types and specifically an array of reactions so that's going to look like this now that we have this state we can pass it over to our reaction selector by saying set reaction is equal to a function call where we get a reaction and then we call a function set reaction to which we pass the reaction like so we're going to worry about the typescript errors or Warnings later on on for now I think we're good we just want to ensure to see the reaction selector on our screen so let's remove this and let's try to figure out how to turn on our reaction selector I think this will have to do with our use effect where we're listening for all of these changes right now here we're listening for the forward slash event and the Escape event but we trigger reactions on the letter e so we can add an additional else if statement and check if e. key is triple equal to the letter e and then we can open up a new block of code and say set cursor state to which we can pass the mode of cursor mode. reaction selector so now we know exactly when to turn this mode on let's also see if there is something we have to do with our other functions like handle pointer move leave or down first let's look into the handle pointer move in here we'll have to add an additional if statement to check if the cursor is not in the reaction selector then we have to update the cursor position so we can say if cursor is equal to null or if cursor state. mode is not equal to the cursor mode. reaction selector in that case we can do all of the calculations we have been doing so far but not if we are within the reaction selector because then the reactions are going to be sticked to the bottom of the screen and we don't care about the cursor position that's it for the handle pointer move finally in the handle pointer down we'll also have to check out if we are currently in the reaction state so right here below update my presence we can set the cursor State and the reason why we're doing this is to check if we are in the reaction mode then we want to set a special property called is pressed to true and here we need to get access to the previous state so we can do that with a callback function similar to what we do in react hooks so we call it like this state of type cursor State like this and then here we can say cursor state do mode is triple equal to cursor mode do rea action so we're checking if it is and if it is we will spread the entire State and set the is pressed value to True else we'll simply spread the state and then we have to close it properly let's just see what we're doing here set cursor State we're closing this one we also need to be closing this one right here and what am I missing I think I was missing one extra parentheses yeah it should be like this this set cursor State we have a callback function like this we check for the reaction we spread the necessary state or we just leave it as it is we'll soon come back to fix those tab script issues but for now let's see what's happening we're updating our state and we also have to do a similar thing on the handle pointer up so this is yet another Handler will create const handle pointer up is equal to everything is the same use callback with react pointer event like this and of course since we're using the use callback we have to properly add the dependency array as well and within it we can do the same thing state is the cursor State and then we simply return it like this we add this special is press property in case we're dealing with the reaction State and we can now add this handle pointer up to our div listeners on pointer up and then have handle pointer up great and we must also not forget to pass the additional properties to the dependency array of the used callback hook in this case we want to make it recalculate the output whenever the cursor state do mode changes or when the set cursor State function changes and that is the same for the handle pointer down as well as the handle pointer up there we go so now we have uh done it properly we're still having some issues with typescript right here so let's see if we can fix it if we scroll all the way up to where we're defining the cursor state which is right here we haven't really given it a proper type coming from live blocks so what we can do right here is Define the type of this state property by giving it a cursor State type like this and save it if we do this you'll notice that no longer we have any issues because now tab script knows exactly what this state is made up of so now we can collapse all of these code blocks so it's easier to see and we're passing all of the necessary handlers to our listeners on the div and we're rendering this reaction selector now within this reaction selector you can see that we also have a tabs script type warning which is also saving us once again instead of just setting the state on reaction which is what we're doing here right now I want to do something else and that is call a set cursor state so that's exactly what we'll be doing modifying the mode to be equal to cursor mode. reaction because that's what we're trying to do modify the reaction we want to give the reaction itself right here so reaction is equal to reaction coming from the state and we're going to set the is pressed to false it looks like I've put a dot instead of a comma right here which automatically fixes it so now that's good but to make a further optimization as well I want to wrap this into a used callback hook in applications such as figma and especially tracking cursors elements moving across the screen we want to ensure that we are very optimized because we can have many cursors moving around the screen and one mistake of recreating this function every time could be very costly so what we can do is copy this block of code put it right here above and call it within a const set reactions function that is a use callback that accepts the reaction we pass into it of a type string and then returns a code block that has an empty dependency array within here we can call the set cursor State and now we can simply call call these set reactions within this call right here there we go that makes much more sense and it's also using the used callback so now we can go into the reaction selector and check if we have done everything correctly and maybe make some small modifications to the code that we copied from live blocks first of all we don't want to use any box Shadows or transitions right here we want to ensure that our emojis appear on the bottom so we can completely re style this div by giving it a class name equal to Absolute bottom of 20 left of zero right of zero giving a margin X of Auto W fit transform rounded Das full BG D white and padding X off two now if we save it go back to the website and press the letter e you can see how nicely these emojis appear in the bottom and once we click on them it goes away so that part is now looking great but of course we're not yet seeing the actual animation so let's figure out how to do it our reaction button file is done but now we have to focus on the flying reaction because that's where we can see all of those flying animations and this file is as good as it can be because we copied it directly from live blocks' examples so now let's go into our live where we're recalling this reaction selector and let's call the flying reaction as well we can do it right here on top below this H1 by mapping over our reaction so we can say reaction. map where we get each individual reaction and then we render a flying reaction for each one just to make sure sure that we can differentiate the reaction from this one we can call this R and then we can give it a key equal to something like r. timestamp do2 string because each reaction has a timestamp we need to give it the X position such as the r.o dox as well as Y which is r.o doy we can give it a time stamp which is equal to r. timestamp and a value which is equal to r. value and of course we have to import the flying reaction so let's see how we have called it flying reaction export default that looks good to me but I misspelled it it's supposed to be flying there we go so now we can automatically import it from our reaction folder and how nicely tapescript lets us know that we have made a mistake that's supposed to be timestamp there we go so now if we save this something happens there we go we're back and if I type the letter e we can see the selection on the bottom and we can select one but still not a lot is happening it doesn't seem like anything is happening once we click it so let's figure out what we need to do next to actually make it selected and follow our cursor and then once we click the left Mouse button it actually flies over the screen with that specific reaction to make those reactions show we can go up all the way up and then we can define a new use interval hook Yep this is a use interval which is coming from our custom hooks uh we just call it like this and then provide first of all the Callback function that we're calling in a specific interval and then how often are we calling it in this case every 100 milliseconds this hook has to be imported from our custom Hooks and I took this one from Dan AB brov's blog where we talked a bit about how to create that react hook which in this case is very useful the only thing it does is it calls a specific function with a specific interval so what do we want to do every 100 milliseconds well here is where that is pressed State comes to play we can check if the cursor state. mode is triple equal to cursor mode. reaction so first of all are we doing any reactions and then and cursor state that is pressed so are we currently pressing our Mouse and if the cursor exists that as well so we want to make sure that all of those conditions are true and then we call the set reaction be careful that is said reaction right here singular where we can call a callback function function that gives us access to the previous reactions and then we call reactions. concat okay what we want to concat well we want to concatenate new reactions so we get an object where we need to specify the point of a new Emoji so we can do that by giving it the xaxis of cursor dox and y axis of cursor doy we want to give it a value which is the cursor state. reaction and a Tim stamp of date. now to know when we are doing it and let's not forget to close it properly so it looks like to me that we have to close one more object right here then an array then one parentheses and one more parentheses to close the set reaction and we might have one extra right here which we don't need and one extra here there we go that's good so we're finally back we're setting the interval and now if we press the letter e and select fire oh my God it's actually firing we can see a lot of fire emojis going every 100 milliseconds if you want to be really crazy you could knock this down to about 20 and then try it one more time and now it's even crazier you can see it's even creating some weird shapes right here which is great I think this was by the flying Emoji or the flying reaction by the live block steam so this is great now that we have that we can bring it to 100 milliseconds I like that you can do something different as well but now not yet will these reactions show to the other person and that's the only goal that's the only reason why we're doing them not to admire them on our own screen but so we can convey our emotions and reactions to another person live in real time so to do that we'll have to use the broadcast hook coming from live blocks we can do that by saying cons broadcast is equal to use broadcast event coming from the live blocks config like so this allows us to broadcast events to other users in the room so still within this if right after we're done setting the state we can call the broadcast hook or not a hook rather What The Hook returned and then pass in an object with the X position of cursor X Y position of cursor Y and the value of cursor state. reaction so now if we do this and press the fire we can see it's going but we still cannot see it on the other screen what are we missing we've done half the job we've broadcasted the events from the sender but we haven't yet received receive them or listen to them from the receiver so what we can do is use another hook and that hook is called use event listener coming from Li blocks config that accepts the event data and then we can have a function to do something with that data essentially it gets fired every time that an event gets broadcasted so here we first get the event by saying const event is equal to event data as reaction event so we know exactly what type it is and then we call the set reaction where we get all the previous reactions and then do the same exact thing so we can copy this entire set State and we want to replicate it to the other user in this case we're not only getting the event data but rather the actual event from the event data that's great and also here it's no longer going to be reaction it's just the value and it's not coming from cursor state that was it for our screen but rather it's coming directly from the event so now once again typescript is quite useful with live blocks because they have documented their types incredibly well so they're here to save us whenever we do some silly mistakes so with that said what do you say that we give it one last chance I opened up the second browser let's try to pull up the event and would you look at that cannot read properties of null reading x happening within our live. TSX line 45 that is right here and yeah that makes sense we have access to the cursor data on the sender browser but we don't have access to it on the receiver end so instead of getting the cursor position we have to get the event position same as for the value right here great so if we try it one more time press the letter e and select some fires and they're actually going up in the air we can make them light up our live block figma clone because soon enough we're going to start converting this live environment inside of which we have the Emojis we have the chat we have the cursor tracking into what is yet to become a figma clone with a fully featured set of canvas functionality now there's one small detail we still have to do to optimize our app when it comes to these emotions and reactions and that is something that you might never think about but it could be very expensive in terms of your app efficiency in the long run and if many users are using the app see when you do a reaction these emojis drop and then they expire or at least you think so they hide from the screen but they are still at added to the state so right now we have hundreds if not even thousands of these saved to the state and we have to clear them so let's do that right now by going above our use interval and use event listener and creating a new interval we can say use interval once again we have a callback function in between it and we can clear them let's say every second here we can remove the reactions that are not visible anymore by calling the set reaction getting in the reaction itself and then filtering them out by calling reaction. filter where we get R for each reaction and we filter it if the reaction or R do timestamp is greater than date. now minus let's say about 4 seconds so we cleared the ones that are completely unvisible by this point now once we have done this you should see no difference in our app everything should still work well but they will be getting cleared in the background so with that in mind we have now implemented a couple of functionalities we have implemented the live reactions of course we have also implemented the cursor chat where you can type something out and finally we have the cursors itself which you can follow and see what the other person is doing now I noticed one small bug and that is if you go into the chat and type something that has the letter e in it then on the center display it appears like they want to do an emotion and not continue typing so that's definitely something we have to fix but before we do that there's one last collaboration tool I want to implement from live blocks and then we can finalize the live features and start fusing them with the figma canvas and that feature is the live Avatar stack it allows you to see who are the other users in your application there we go and we're going to implement this on the navigation bar of our app so we can get started by copying this Avatar code which is a very small piece of code just containing an image of the Avatar and we'll create it within our components within a new folder called users and then within users we can create a new avatar. TSX inside of which we can paste the code that we copied we can delete the comment and just make our life a bit easier of course in this case we want to figure out what exactly will we show and here they pass the name and the source as props to this Avatar component so with that in mind we have to see where this component is getting called so we can go to the index file right here and find the place where we're calling it so let's copy this entire example go back to the code and then create a new file within users called active users. TSX and within there we can paste our function example of course we can turn that into a const and call it active users is equal to an arrow function where we return all of this and then we immediately export default active users great and we must not forget to do a couple of imports we want to import use others from Li blocks config we want to import the use self from the config as well and we want to import the avatar from that/ Avatar I believe this is it for now we do have some more Styles coming directly from Li blocks so let's get the Styles as well in this case we have this avatar. module. CSS directly within the Avatar so let's copy that file by calling it right here Avatar r. module. CSS and past it and the second one is just the index. module. CSS so we can also paste it right here index. module. CSS and paste this right here that allows us to call these files we can call the index within this active users we can import Styles coming from SL index. module. CSS that's for the active users and for the Avatar we can import styles from Avatar module CSS great I believe that's going to be it from what we need for this code so now we can go back to our current application and actually use these active users within someware and where else are we going to use them than within our navbar so let's create a new component within the compon component folder called navb bar. TSX and run rafc this is going to be our Navar since I don't want to waste your time on creating the Navar I'm sure you've done that hundreds of times so far I'm going to provide you with the code to the starting version of the knobb bar in the read me down below so just find it and then override what we have right now you'll notice that it is a simple navbar we have to fix our import for the navbar props so just manually or automat atically imported once again and you can see that we just have a simple nav and we can now use that nav bar within our page so let's navigate over to the page. TSX and right above our live we can now put the Navar right here by importing it from components Navar and you can see that now it's struggling to get the Avatar or to show it so we can go to the nav bar and you can see that we are using the active users component right here and within active users we are displaying our Avatar but it looks like it cannot get access to the current user. info. Avatar so what we can do is just kol log it to see if we're getting something back from the use self hook console.log and I'm going to conso log the current user if we do this and open up the inspect element and go to the console scroll up we can see that we do get back an object that contains some information but the ID info and everything else is basically either false or undefined and that's because we don't yet have any users in our application we haven't done proper O So for that reason we're going to modify the way that we show this user a bit or this Avatar what we can do is say if there is a current user then we're going to remove moove this div and we're going to only show the Avatar component like this but to the Avatar we won't be passing any kind of source we're going to figure out a source programmatically within the Avatar component instead we'll pass a name of U and we'll pass some other styles such as a border of three pixels and we can also pass a border primary green so we know that's us and we can show this current user on top because that is us so we can do that right here below this div current user is first now we also want to modify the second time we call the user or the Avatar here we want to pass another source we don't have it but we can pass the name of a call to Generate random name which is coming from lib utils if you check it out it just finds some random adjectives and random animal names and then puts them together I believe live blocks created this example so now we can go into the Avatar and here we can also pass the other styles equal to minus ml-3 so now we can go into the Avatar and accept these new props such as name that we already had as well as other styles and now that we have this image we can just render a proper Source in this case we want to spell out a URL coming directly from Li blocks so we can say https colli bloks.io SL avataravatar Dash and then math. floor math. random inside of it times 30 and then we enclose it with a BNG so this is going going to generate a random image instead of using a regular image we can use a nextjs image tag by importing it from next image in that case we don't need a size we can simply say fill and we're already taking the class name from the Styles we also need to provide it an Al tag which is going to be the name and let's also provide additional class names right here by making this a template string that accepts the styles. Avatar then we can also give it the other styles like this and finally we can set the height to nine and width to nine as well and close it right here and we need to modify the types in this case we no longer have the source we have the other styles of a type string now if we save this and reload you'll notice that we have an error importing jspdf okay why all of a sudden we have to do something with jspdf I think that's because we use this Generate random name function which we imported right here from utils and because of that it might have looked into the Imports of this file and we didn't yet actually install this package so for now we can comment it out and we can comment out its use right here at the bottom we're going to bring it back later on but once we do that we should be back to our application and now nextjs gives us an error saying that we cannot use images whose sources we didn't validate so we can go to our next config JS and within here we can add images that's an array and here an object within it where we specify the protocol of htps we specify the host name say that the images will come from live block.io and we specify the port of just an empty string now if we save this this and reload you'll see the app broke so we just have to run mpm runev one more time and how nice nextjs automatically lets us know that we have misspelled or mistyped something in our config so it's not typescript saving us right here it's nextjs itself it's saying that it expected object at images and we provided it at aray so the images itself indeed has to be an object but inside it has to have these remote patterns which is then an array that we have created right here so we can copy this and instead of putting it directly within images we can put it right here under remote patterns and that allows us to rerun our application but then also I can notice that I said post instead of Port right here so thank you nextjs and versel if I switch this up right now you can see that we are live on Local Host 3000 once again and hopefully we'll be able to see something oh would you look at that okay this is exciting so we can see our Li block figma clone but at the top we can see our fig Pro we got that immediately because I use this starter for our navigation bar that contains the logo image and then we have the active users which we have developed letting us know there are two different users on our app right now we have you which is us and we have a bright penguin so now we can kind of make it fit better with our current application and not make it look like this it should always be sticky at the top of our app so let's do that first before we check out all of the Avatar functionalities to fix it we'll have to go to our page. DSX and we'll have to modify the styles of our div or while we're here let's call it a main because this is our main div for the entire application we can give it a class name equal to h- screen meaning it will take the full height of the screen 100 VH and we can also give it an overflow of hidden which will remove this ugly scroll on the right side if we save it now we can see only the nav bar which is taking the entire screen and we cannot scroll anymore so now let's figure out why that is the first thing we can do is wrap the live part into its own section so that's going to be a section with a class name equal to flex h-4 and then Flex Das row and we can put the live right within it if we do that that's not going to do anything on its own so we have to go into the nav bar and then into our active users here we copy the code from live blocks as example and they use the H screen which in this case we don't need we don't want it to take the full screen and now we have a more regular Navar but let's fully style this exactly as it should by modifying this div right here and giving it a class name of flex items D Center justify Das Center and a gap of one there we go now they appear on the right side we're checking for the current user then we're checking if there are any other users in this case we don't need the info and finally if there are more users then we display a text of more which in this case we don't have anymore now there's one weird thing happening once we move our cursor around and that is that the colors change we don't want them to change because we want each user to have their own color so to fix it we can memorize the active users component we can do that by using the use memo hook so we can take this entire part that has to do something with the users the actual jsx code take it C copy it move it from here and then create a new variable right here on top called const memorized users is equal to use memo hook coming from react where we can simply return what we copied right here so the return of this div make sure to add a return statement first there we go now the second parameter to the memo is when will it actually refresh and it will refresh refesh when the others join so we can say others. length changes then we will change the colors and for that we also have to know when the others have joined and instead of using the others we can use the users right here to see when the length of the users has changed finally we can return the memorized users instead of always changing those users that way once you move around you're going to notice that they're not changing at all which which is exactly how it should be but if you reload sure you're going to get new colors but the most important part is that those users are exactly the same color for the entire duration of your session so now we can nicely see that there are two users currently logged in we can even chat with each other by typing something out or if we're really happy we can also show some emojis so this is looking great and we can see that the other user is online with us now let's modify these Styles a bit and to modify the Styles we can go to Avatar module CSS and we can simply change the width from 56 to something like 40 pixels there we go that fits a bit better and we'll also have to add some padding and we'll do that not within the avatars but within the active users so that's right here in the div by giving it a padding top of something like let's do eight pixels and instead of padding top we can do padding y meaning top and bottom and this is looking great with that said do you remember that issue where if we start typing and type the letter e immediately the chat disappears let's go ahead and fix that the fix for that will be within the cursor chat so let's go to cursor chat and then to this div we can provide a special property on key up that's going to accept a callback function that gets the event and we simply need to stop the propagation meaning stop doing other stuff and focus on the typing so e. stop propagation if we do this and open up the other window and start typing something like test you can see that we can actually type test without being interrupted by the Emoji but now if we close it with Escape key and type the E key we can actually show the Emoji I think this would be the perfect time to show what we have done so far with all of the live functionalities in the full screen so I will expand these browsers one here and I'm going to take half the screen and take it for the other and as you can see two different screens you can think of these as two different users on two different browsers on two different computers they are looking at the same application at the same time in the browser they can see each other's statuses and they can start chatting hey let's design this app and immediately it works in real time and they can also share different emojis so with that in mind I'm going to close one browser which means that we have implemented the majority of our live functionalities using live blocks and the reason why I wanted to do this first before doing the full figma clone is to show you that you can do this on absolutely any application what is our app even right now it's just a simple piece of text but still a piece of text powered by all of these real-time collaboration functionalities so it can be a dashboard where you can communicate with other people viewing it at the same time it can be a landing page like what versel is doing with her comments where you can leave comments to request some changes it can be like what figma is doing to collaborate on creating the design and that is exactly what we're doing in this video and you will love learn how to do it all so for now I'm going to close all of the currently open files to have a clean working environment and we'll start converting this piece of text into a fullblown canvas on which you'll be able to add elements and interact with them in real time with another person so I wanted to say the exciting stuff starts now but this entire thing has been incredibly exciting to me but let's say that the second exciting part of this video starts now to get started with creating our canvas we can go to our page. TSX and we can start focusing on the layout of our application such as the left sidebar and the right sidebar and then the canvas in the middle so let's do that right away let's put the left sidebar right here on the left side or on top by calling it a left sidebar which is a self-closing component and we can duplicate it and turn it into a right sidebar right here at the bottom next to the live there we go so this will temporarily break the application but at least we know that we have the structure ready what we have to do next is actually create those two components so let's go to our components and create right here in the root of the components the left sidebar. TSX where we can run our afce as well as the right sidebar where we can call rafc as well this leaves us with the ability to import those two components and then reload the page to see what we have although you cannot see it very well right here we have left side bar and on the right side we have the right side bar while we're here we can also give them some additional styling so let's turn them into a section and and let's give this section A Class name equal to flex flex-all border DT as in top border D primary D gr-200 BG D primary D black text- primary D gr-300 Min dw-200 and 27 pixels within square brackets sticky left Z z h full so it takes the full height there we go it's already starting to take effect on Max small devices hidden select none overflow dy- Auto and a PB of 20 for padding bottom now we cannot seem to see it on small devices but if we make it a bit larger and even larger we can see the left sideb bar up appear right here on the left now let's focus on the right sidebar two by navigating to our right sidebar also turning it into section and what we can do is basically duplicate most of the properties from the left sidebar so let's just copy this entire section and paste it right into the right sidebar of course we'll have to rename this from left to right and also while we're here let's create an a H3 within it and this H3 will say design so the right side is for the design the left side will show all the elements so let's give it a class name equal to PX of five padding top of four text- Xs and uppercase there we go that's better and we can also modify this in the left sidebar by copying this H3 pasting it right here within it and saying something like elements there we go that's better already now this is starting to look more like a finished application but what's missing is that this middle part is not yet a canvas so what we have to do is go back right here to our page and we have to go into the live and within it we have to render a canvas so we can go all the way here to the top where we have figma clone we're going to remove that and we're going to render a canvas element this doesn't have to be imported this is just a regular HTML 5 canvas now this canvas doesn't seem like much at the moment it's still just an empty screen that's because we have to initialize all the refs as in references to make this canvas do something so back in page we have to create a canvas ref we can do that right here at the top by saying const canvas ref is equal to U use ref which has to be imported from react and it's going to be of a type HTML 5 canvas element like this and at the start set to null we'll also have to create another ref for our fabric Library so we can say const fabric ref is equal to use ref and this one will be fabric. Canvas OR null so this is a special type that we have fabric. canvas and this fabric has to be imported at the top from fabric so we can do it by saying import fabric from fabric now we are properly specifying the types and at the start it's also going to have the value of null the canvas ref is the reference to the canvas element that we'll use to initialize the fabric canvas and the fabric ref will allow us to perform operations on the canvas it's kind of like a copy of the created canvas so that we can use it outside of the canvas event listeners and there's a third ref we'll have to Define const is drawing it is a Boolean variable that tells us if the user is currently drawing on the canvas or not meaning if the free form drawing mode is enabled so we can say this is equal to use ref at the start set to false and finally we can use all these refs to initialize our fabric canvas we're going to do that within a use effect so we can start a new use effect and make it load only at the start meaning we're going to leave the dependency array empty for now we can import use effect from react and we can start creating the canvas by saying const canvas is equal to initialize fabric which is important from blib canvas and to it we need to pass the canvas ref as well as the fabric ref that looks like this and we have to put that within an object because initialized fabric only accepts one parameter now that we have our canvas we can listen to Mouse down events on the canvas which are fired once the user clicks on the canvas and we can do that by saying canvas.on Mouse down and then here we have a callback function that gives us the option to make a decision what we want to do once that happens and here we want to call a utility function called handle canvas Mouse down to which we can pass an object containing the options the canvas the is drawing property and later on a few additional properties as well and we also have to pass a few more additional properties such as selecting which shape are we currently interacting on since we don't have the possibility to choose a shape right now we don't have any shapes we just have to create a ref for it a reference so we can say const shape ref is equal to use ref that's if a type fabric do object or null at the start set to null this is a reference to the shape that the user is currently drawing and we also need to have one for the selected shape so we can say const selected shape ref is equal to use ref of a type either string or null at the start set to null the selected shape ref is a reference the shape that the user has currently selected so if it's a rectangle it will say rectangle now we can pass these two additional refs right here by saying shape ref and selected shape ref and this mouse down will be incredibly useful once we start dealing with the elements on the canvas but for now we care about one even more than this mouse down which is a resize element window dot add element listener of a type resize and once we resize it we call this callback function that calls the handle resize utility function to which we can pass the fabric ref and this is not element listener it is add event listener so we are listening for the resizes of the canvas such as once I do this then the size of the canvas changes but now the question is what do we do with this it doesn't seem like we have a canvas at all well we're going to put it all together right now remember this canvas R we can finally send it over as a prop to our live component and then go into it accept it as a prop right here of course we have to define the type while doing that so we can say that that's going to be of a type props and right here we can say type props is equal to Canvas ref of a type react. mutable ref object of a type HTML canvas element or null in case at the start it is set to null now tab script is not going to complain and we can use this canvas ref and pass it over to our HTML 5 canvas component which is right here canvas ref is equal to Canvas ref or rather just saying ref is equal to canvis ref and another very important thing is that we have to provide the ID of canvas to our div that's wrapping the canvas now if we go back we can actually try to interact with it because we have created this mouse down functionality but on Mouse down we have to pass specific shape ref letting the canvas know what kind of element or shape do we want to create at the start it set to null and we don't have access to any kind of a selector to choose which kind of element we want to add we'll create that selector within the knob bar very very soon but for now let me show you how to quickly get your first element on the canvas so you can feel like a real artist the only thing you have to do is modify the selected shape ref initial value to something like rectangle that way once you click on it we're going to pass it and the handle canvas Mouse down which is our special utility function will just create this specific shape based off of the selected shape bre so click and reload your screen and press on it there we go so now every time that you press you're going to get a 100 by 100 pixel wide rectangle and then you can move those around you can see that works right of the bad but right now once you click you're just going to get as many as possible this is looking great it already feels that we can create something and we can even move them off you can see well not really right now we're kind of a bit blocked and it's creating so many as soon as we move move it around and keep our Mouse down this is just the first prototype version don't worry we're going to fix it but you can see how it works right now you get the idea now what do you say that we add a selector to the navbar that allows us to choose between many different shapes such as these ones right here rectangle circle triangle line even an image upload and a free drawing we can do all of that by adding these couple of icons to our navigation bar so going back to the code we can revisit the navbar component we have a Navar with our logo and right below or rather right to the right side of the logo we want to add some additional elements so we can create a UL component for an unordered list give it a class name equal to flex and flex-r and right there we can map over all of our nav items I've already created a constant called nav elements that we can simply use and then import if you quickly go to it you can see that it contains the select the rectangle text all different kinds of icons we want to show on our nav bar and we can simply map over it by saying that map where we get each individual item of a type active element coming from types or any and for each one we want to immediately return An Li please make sure that here you put a parentheses and not the opening of the function block because then that's not an immediate return now with this Li since we're mapping over different elements we want to give it a key equal to item. name then we also want to show something within it now to be able to show something within it we have to figure out what we're mapping over for all of these different icons that you can see on the deployed version such as click Text delete and so on they have a single value that that is a string but this rectangle one has a subarray of different elements we can show such as rectangle Circle and so on so let's first deal with that one we can do that by checking if the sublist is an array so array do is array and then we pass the item. value to it if it is array then we want to return a special component called shapes menu like this it is a self-closing component that's going to render all of our items of course if we save it now it will break our app because we don't yet have this component but before we create it let's just follow through with the rest of the structure if it's not an array then we can check if item question mark. value is triple equal to comment if it is equal to comments then we can open up a new turnery and return something known as a new thread this is also another not self-closing component that will create and finally else if it's not a comment and if it's not an array then we want to render a regular button for now I'm going to leave it just a basic button later on we're going to turn it into a shat CN button so now we have to create the skeletons of these few components and throughout the entire build of this application there are going to be many components which are just simple drop- down menus and and once again I'm here to let you experience the build of the figma Clone on your own but to save your time and to make your learning focused on exactly what you need for building figma and not building drop downs or nav barss I went ahead and prepared a couple of components for you so you can more easily follow along with the video a folder containing just a few of the components we'll be using for this build will be in the Read Me Down Below download it unzip it and then drag and drop it to your component folder folder it's going to be called new components and from it you can simply extract all of the files into the components folder such as the comments folder the settings folder the shapes menu that we need and then two of these components that we already have which is the left side bar which we can replace and the nav bar which we can replace too so now that we have consumed all of these components we just have to make it work together you notice we have a lot of warnings right here here with Imports so let's fix those for you this part might already be fixed but in case it isn't we just have to make it say add slash constants and add slash types this component called new thread we don't yet have so we can create it by going to components then creating a new folder called comments and then within the comments we can create a new component called new thread. TS SX run RFC and an important thing is that this one will accept children as it props as it will have some elements within it so for now we can simply return children that's the only thing that we have to do for now and you can notice that it is a named import so we can just say export const new thread instead of saying export default so now if we do that all of the Imports are good besides the UI button that's because this button is a Shad CN component so this is the first time that I'm going to show you how to install a shaten component going back to shien's docs you can search for a button component there we go just a simple button and you just need to copy this installation command copy it and then within a new terminal paste it MPX shaten UI add latest add button this command will ask you now whether you want to install it and in a couple of seconds it will automatically be installed so if we fix this path right here you can see that now it's properly getting the button and with that our Navar should be good but we're not yet ready to see our application because of some of the other issues that we have in our left sidebar so in our left sidebar we also have to fix the path or maybe it was already working for you and we have to move into the shapes menu which is if you remember the component we started to work on here we have to install another component from shaten which is the drop- down menu so let's search for it drop down menu and the installation command is everything the same but this time we add a drop- down menu instead of a button while that is happening I'm just going to fix these Imports for me and everything should be good with the menu as well besides this one small last thing and that is that if you go to the shapes menu you can see that we're trying to get the value out of the active element and that is that if you go to the shapes menu you can see that we're trying to get the value out of the active element right here and our shapes menu is expecting it and our navbar is expecting it as well but we're not yet passing it from the homepage so right here in the homepage where we're calling our Navar we have to pass some additional props to it specifically now we're wondering about which element is currently clicked from the nav bar so right at the top I can create a new use state property and I'm going to call it active element set active element at the start set to an object that has a name which is an empty string it has a value which is an empty string and it has an icon which is also an empty string and we have to import use state from react we can also Define the type as active element coming from types and now that we have this active element we also have to do its counterpart which is a function called handle active element which will allow us to choose different elements from the Navar so we get one parameter which is LM of a type active element and within here we can set active element Two element that we pass in and for now we can set the selected shape ref. current to be equal to LM question mark. value and say as string there we go now we can pass the active element and the handle active element into the nav bar so let's scroll down and expand it and to it we can pass the active element equal to active element just as a prop and as the second one we can pass the handle active element equal to handle active element now moving into the Navar we are properly accepting all of those props at the top later on there's going to be a few more and would you look at that the full Navar is now complete I know we brought it in with some of the other components but it is looking great we can select different things for example this is just the cursor and then we can click right here to open up the selector for different elements so now if I click the circle and if I click right here it's going to do a circle if I select triangle it's going to do a triangle you get the idea right we can do also rectangles as well as lines all of this is working incredibly well but for now we just have a listener to be able to do the mouse down we cannot yet move things although yeah we can but then it adds additional ones once we try to do something more complex so we're just getting there we're doing things step by step the free drawing is also there but we can improve it still but already this is huge work that we've done so far but now let's back to what we have been working on for this entire time making this functional in real time so now if I reload this page and if I start adding some rectangles right here you can notice that they don't get added on the other screen of course we can chat and say hi and even do some emojis as we learned before but unfortunately the main part of our application which are the actual elements we're adding on the screen don't show up on the other screen and for that we're going to utilize the last live blocks feature of the day called storage we're going to store the session in real time that's going to keep track of all of these elements their positions sizes colors and more and then we're going to replicate that session within a li blocks room so that both of these users can share the same board to make that happen I'm going to close everything we have right now and start focusing on implementing liveblock Storage liveblock storage realtime data store for collaborative documents or in simple words it's basically a place where we can store documents or any kind of data needed for realtime collaboration in our case we'll be showing fabricjs elements that we want to show to everyone in our real-time room so if somebody is creating an element like I am on this screen right here everybody that's in that room should be able to see me move elements create them change colors and do everything else in real time not to mention that we'll also soon be able to implement their history API that allows us to undo or redo some actions so with that said how do we implement it let's clear this right here so we don't have too many elements and let's navigate to our page. TSX right at the top of our page we'll use one hook that hook is called use storage and that use storage Hook by live blocks allows us to store data in key value stores and automatically sync it to other users or in other words we create a subscription and updates to the selected pieces of data so let's say const canvas objects which is going to be equal to the use storage hook coming from live blocks config there we have a callback function where we get the root and then we can say root. canvas objects this is just a selector so if you hover over it you can see that we extract arbitrary data from liveblock storage using an arbit selector function so now we can navigate to the use storage hook within live blocks config here is where it's being mentioned and if we scroll a bit up where we can find the storage here is where we can add some fields that we need to keep track of so in this case we can add a field called canvas objects which is going to be something known as a live map and that's going to be of a type string and any this live map has to be imported from the live blocks client it's just a data storage option so here we can see that the live map is similar to JavaScript map that is synchronized to all clients it's essentially key should be a string and value should match the Json structure so now that we have this you can see that our use storage is no longer complaining because it knows that canvas objects now is a readon map so now that we have these canvas objects object let's find a way to actually update them and the way we do that using Glide blocks is by using the use mutation hook so we can say const sync shape in storage so we sync the shape that we created and we call the use mutation hook coming from Li blocks config where we can immediately destructure the storage like this and then as the second parameter we get back the object and finally we can open up a function block now let's just ensure that this is good we have the use mutation we destructure the storage and then as the second parameter to that use mutation we put the object oh looks like I closed this one too soon right here is a comma and then after we're closing it so I think we are good now but we have to close one more right here and then we must not to also forget to pass a dependency array to the use mutation hook if you open it up it's going to say that first you create a callback function that lets you mutate a live block State the first argument gets passed into your callback with mutation context and here they give you an example like you can change the color of something with the field layers you can also delete layers and do some other actions like in this case we simply want to first check if we have an object we're working with so if we don't have an object we simply return but then if we do have an object we want to destructure its object ID by destructuring object ID from the object then we want to turn the fabric object into Json format so that we can store it in the key value store by saying const shape data is equal to object. to Json then we get the shape data. object ID and we set it to object ID so essentially once we convert it into Json we make sure that the object ID of the adjacent object is set to the correct object ID then we need to get canvas objects by saying const canvas objects and that's equal to storage. getet Canvas objects so essentially we're trying to pull existing objects from the liveblock storage finally we call the canvas objects and call the do set method on it it's provided to us by Li blocks and it allows us to set a value so now we can set the object ID of the shape we're syncing and we can make it equal to the shape data that we're passing over and this will sync the shape in storage now the question is where do we use this sync shape in storage well we'll have to use it in many places such as when moving elements when deleting them when clearing out the entire canvas but first let's call it on move so far we have developed one part which is this mouse down but now below the mouse down we also want to develop the mouse move so let's duplicate the canvas that on Mouse down make it to Mouse move and then handle canvas Mouse move which is another utility function coming from lib canvas to it we can pass the options the canvas the is drawing the shape ref selected shape ref as well and finally we can pass the sync shape in storage so after moving it this function will take this sync shape in storage and it will call it with the specified element so now if I go back and create a new element like a rectangle we get an error let's see what the error is saying canot 3 properties of undefined reading set under cannabis objects so now if we go back right here to where we're calling that sync shape and storage it's complaining that it cannot get the canvas objects there's a couple of reasons why this might be but let's take it step by step first I want to move into the room. TSX file and here we have to do some modifications as you can see we're defining the initial presence right here and and it wouldn't be bad to define the cursor as null at the start to define the cursor Coler as null at the start as well as defining the editing text as null at the start as well not related to storage but again it's good to set those default values now we can define something that's more important for the storage which is a new property below initial presence called initial storage and that's going to be equal to an object that's going to have the canvas objects in there equal to a new live map which is coming from live blocks client and we call it as a function or as a Constructor in this case rather so now that we do this we have some initial storage to work with and while we're here I just noticed that we're using the old loader so we can immediately call our new self-closing loader by importing it from components loader now if we load you'll see this nice looking loader at the cent of the screen and we can try adding a new element as you can see now we're not getting any errors which is much better than before but this is definitely some kind of a weird situation that we're having right here it almost feels like we're doing some kind of a Fibonacci Sequence or like a golden ratio with these cubes the way that they end one on top of another so before we continue doing this we might need to fix up our on Mouse down or on Mouse move events so let me go back to the page and let's continue writing all of our listeners so in this case I'm guessing that we're missing the mouse up so I'm going to take this and duplicate it one time below modify to Mouse up we also need to pass it the options the canvas the is drawing we're providing the shape breath the selected shape gra the sync shape in storage and also we have to provide another function which we created before called set active element and we pass all of those to handle Mouse up like this of course let's spell it properly handle canvas Mouse up there we go in this case we don't need the options and we're missing something known as an active object ref this ref is the one that we didn't create yet so we can create it right here on top by saying const active object ref is equal to use ref fabric object at the start set to null and the reason why we're passing it into this function if we look into the handle Mouse up you'll see that we are resetting the active object ref to null and the reason why we need this active object in the first place is so that once we click on a specific element we know which one is currently select selected so with that said we're now done with the handle Mouse up with the handle Mouse move as well as the handle Mouse down and now would you look at that we can actually create one element it doesn't create many more once we do that just one or if you want to you can create more and now we actually have the mouse up and mouse down which means that it doesn't just create a 100 by 100 pixels rectangle you can actually drag it around to create different sizes of those rectangles and you can move them around and they all look great there we go that's a little winky face for you for coming this far into the video now with this in mind there's one question that remains and that is is this synced to the other browser so let's check it out if you reload the screen and reload this one too besides that great looking loading you can see that the ele elements are actually not getting synced yet so there's still some work we need to do before we can actually see the elements on the other screen and what we have to do is rerender the canvas every time that there are changes in our how are they call canvas objects we have to rerender our canvas to show those new changes on the other screen so to render our canvas we can just add a use effect and this use effect has a callback fun function as we know and will rerun whenever the canvas objects change inside of here we can call the render canvas function coming from lib canvas and to it we can pass a couple of arguments to see what parameters it wants we can control click into it and we can see that we need to pass the fabric ref the canvas objects and the active object ref what we do with it is we simply Liv all of the objects which basically removes them from the canvas I really took my time to nicely document and comment out all of these features so in case you want to dive deeper into how all of this works so you can see it all right here so first we clear the canvas and then we render all of the objects from the storage that's it that's the magic of how this works so if we go back we need to pass it the fabric R we need to pass it the canvas objects and finally the active object ref if we do that and save it we can see all of these already after a refresh which is pretty amazing but now we might have a bit too many to be able to see what's happening let's kind of move them a bit down on the screen oh and would you look at that we have too many on the other screen as well that is great but just so we don't have too many right now while we're working on it and while we don't implement the clear function we can go back to the light Block's dashboard and clear the storage there so if you go back to your project and then rooms you'll be able to see one room that we have and then you can go to that specific room and you can see the canvas objects storage how cool is that what we can do here is just delete it and that way the storage for this specific room will be deleted so now going back it's completely empty and we can reload the page with that in mind if I now create an element right here you can see how cool this is we can automatically see it being created on the other screen in real time and I was just about to say that the moving works as well but if you move it around you can see that right now everything is being synced besides the moving part so to do that as well we just have to add another listener such as canvas.on object modified we again have the Callback function of options and then we can call a new handle canvas object modified and to it we provide the options as well as sync shape in storage if we now save this and collapse it if we move the element around you'll be able to see it move as well and that's exactly what happens this is great you can now resize the shapes modify them create a lot of stuff in this single canvas but this is still only like a simple mirror board or something like that like a whiteboard but this is a real figma clone and we will provide many more additional functionalities on top of just creating some elements and moving some blocks around will allow you to create real interfaces even do free form drawing and upload images more that soon but before that we cannot have too many elements on the screen so what we need to do is implement the delete the delete and clear functionalities and to implement the delete or the reset we have to connect it to the delete and reset buttons on the navbar so if we go right here to this SV bar you'll see that we are already passing the handle Act active element and that handle active element is being used right here on this button handle active element with a specific item so it's going to trigger a specific action but now it's up to us to actually Implement which action does this handle active element do right now it's simply setting the active element and setting the selected shape ref current to that element's value but now now within here we also want to implement a switch statement where the key is the element value and within it we want to check if the case is reset in which case we want to delete all shapes from the canvas so here we can call the delete all shapes which is a special mutation function that we have to create right above the handle active element so we can say const delete all shapes is equal to and once again we're going to use the use mutation hook coming from Li blocks where we get the storage and then if I manage to close it properly this time we need to pass we need to open up a function block and then pass an empty dependency array at the end this is how we do use mutations in Li blocks so right within here we need to get the current canvas object object from the storage by saying const canvas objects is equal to storage. getet Canvas object then if the story doesn't exist or it's empty so if no canvas objects or even if it's empty right now right here canvas objects we can call the do size property on it since it's a map is triple equal to zero we can simply return true as in deleted then if that is not the case we can map over all of the canvas objects by using the four off property so for const we destructure the key and the value of canvas objects do entries like this and then we simply call the canvas objects. delete and then we delete a specific key or in this case all of the keys finally we want to return canvas objects that size is triple equal to zero this will return true if the store is indeed empty so now on the reset switch case we're calling the delete all shapes there's also an additional thing we have to do which is not only clear them from live block storage but also clear them from the existing canvas by saying fabric ref. current do CLE and we can set active element to be equal to default nav element coming from constants and then we break it so this is it for the reset and this default nav element is just basically the select that means that if we delete everything we'll be able to just move to the select so we can continue moving some stuff so what do you say should we test reset reset is connected to this button right here here that has the arrow following its tail so if I click it it's gone and on the other screen it's gone as well this is great oh but we have some kind of a bug where on the current screen we can see that the elements reappeared let me just reload one more time on both browsers it looks like it's empty if I create a new rectangle it works it moves and I can clear that's good if we need to fix some bugs we're going to fix them in the future for now it's looking good now alongside reset what do you say that we also implement the delete functionality so right here we can add another case in this case a case of delete and call the handle delete functionality which is coming from our utility function from lib key events we can call it like so to it we need to pass the fabric ref. current as any just for typescript and as the second parameter we have to pass the similar thing what we're calling here which is a mutation in this case to delete all shapes but in this case to just delete a single shape from Storage delete shape from storage and now we can create that function right here below delete all shapes cons delete shape from Storage it's also a mutation that looks like this that accepts the storage which is destructured and the object ID we have to close it like this and also add the dependency array and then within it we simply want to say const canvas objects is equal to storage. getet Canvas objects to First retrieve them or to retrieve that specific one and then say canvas objects. delete the object ID that's it that's how we delete it now we're passing that to our case of delete and once we delete it we can also set active element to the default nav element which is Select once again and it looks like I have an extra column right here after the case we are back within our app we can create a new shape let's do something like a circle this time and let's try to delete it I can click on it and click delete and it looks like it doesn't get deleted let's see what else can we do to make it happen I think that there's a special function in fabric.js that allows us to dispose of a canvas so it's a method that clears all the events and listeners and everything and allows us to kind of put it to trash here is a documentation page for that element not the prettiest one I know but it's a function called dispose that essentially clears a canvas element and removes all of its event listeners so without it it's going to be hard to properly delete an element so if we go to this use effect where we initialize the canvas and its listeners right here we can return as we usually do in react to to do some cleanup and we can call the canvas. dispose right here this will help with the deletion so if we go right here if we create a new element and now if we click delete after selecting it it actually delet it so we can open up the second browser maybe we can even create some stuff right here and let's try to delete one that works let's try to create two one and let's do a triangle there we go and let's try to now clean up everything that works as well so now not only we can have the selection right here we have the rectangle circle triangle line and free drawing as well and we can also delete individual elements or reset the entire canvas we are getting there we're just building features of the features now that we have the live function functionalities and now that the entire canvas is set up I think it's been quite some time until we have seen both of our sidebars so what do you say that I extend this just a bit so we can see our sidebars especially I'm wondering about the left sidebar within here we'll show all of the elements that we are creating in our browser such as this nice looking shape and also all of the other elements circles rectangles pieces of text doesn't matter they should all show up right here and doing that couldn't be easier the only thing we have to do is go back to our page which is where we just were and right here we have to pass the canvas objects remember the canvas objects are all of the objects we're storing in live blocks storage and we have to pass them to our left sidebar as a prop as a matter of fact we can look into the left sidebar and see that it's accepting all shapes which is an array of shapes so the only thing we have to do is say all shapes is equal to array. from and then we pass the canvas objects the reason why we have to do array from is can you think about it it's because the canvas objects are a map a special structure which is similar to array but not exactly the same so this way we create an array from the map now if we save this and expand our browser you'll be able to see something truly great we can see our layers you can hover over them and see the rectangle Circle and more and as you move them they stay the same but as you create new elements like a triangle you can see how it shows at the bottom of that list so if you keep creating new elements let's add a line you can see how also it appears below and you can keep doing that indefinitely all of the elements are going to appear at the bottom so you have a clear history list order of all the elements you've added and talking about history wouldn't it be great if we implemented undo and redo features allowing us to be able to go one step back or one step forward as you can do in every single modern whiteboarding designing or documenting app like word figma or even Meo or figjam so let's Implement undo and redo features and I noticed that sometimes I get this error with the width the canvas width remember we said that we're going to fix that later on so don't worry about it too much now live blocks makes implementing undo and redo functionalities super simple the only thing you have to do is use their hooks so right here at the top we can say const undo is equal to use undo coming from Li blocks config that's a hook and we can do the same thing for redo so we can say cons to redo is equal to use redo just like that and of course we need to import both of these from Li blocks config these hooks allow us to do special mutations the only thing we have to do now is call them and the question is when will we call this mutation we can do it where we have all of the other event listeners on the canvas but this one will be just a tiny bit different as we have the resized listener right here we can add a similar one window addevent listener and this one will be listening for the key down event where it's going to get the specific key that we pressed and then we can call the handle key down coming from lib key events and to it we can pass the event that we clicked we can pass the canvas which is a ref to the fabric ref. current we can pass the undo and redo mutations as well as sync shape in storage so we can properly sync it and then also delete shape from Storage which we created before in this case you can see it's complaining about the type so in all of these options as well as the event we can just say any for now that's that way it's going to be good and in this case we don't even need the options great so to figure out exactly how this works let's navigate into the handle key down and see what we're doing with this event or these mutations if you scroll down you can see that if we press a key such as a control key or command key and then a letter C it will copy it if we pressed command or control+ V then it will paste it but in this case we're wondering about Z or Y in which cases we undo or redo so I just created this special key events function which figures out which key you pressed and then based off of that it does a specific mutation in this case we're simply calling the undo redo functions provided to us by lightblocks so now if we save this and collapse it we can see if it actually works I expanded my browser and now I can see 16 errors so now for sure I know why this area is happening it's happening when we resize the width of the canvas we're going to fix this right away but before that let's clear out the canvas and let's test out our apps new functionalities I'm going to create a rectangle just by clicking right here and maybe let's add a few more things like a circle and let's also add a triangle now we have those three different layers and if I delete some of the elements by pressing on them and clicking here now let's say that we didn't want to delete them and we want to get our elements back you can press contrl Z and it will revert all of the actions what you can also do is maybe resize them like this so now if I press contrl Z it goes back you can do literally anything you want such as moving the elements as well which works incredibly well so let's try to create something like maybe a couple of shapes right here and put them together let me even change the size of this one right here and put this one here there we go that's looking great and now we can use a control Z to get back and we can even use control or command y to go fully back to our creation this means that everything is working well you can delete the elements you can get back and it just works great that was undo redo and history and there's one small bug you might have noticed before when creating any elements randomly a rectangle would appear That's because if you remember correctly if you go to the page. TSX the top of it before we were playing a bit with the selected shape ref by default setting it to rectangle in this case it must be set to null so this will fix any kind of small box that we've had in our application and make it even more polished with that said we now have the left sidebar with complete history and the list of all of the elements as well as undo redo functionalities one thing that believe it or not works out of the box is our text element field so you can click on the text click right here and you can immediately enter text elements and just write some stuff for your website for example this is the website hero section you can say welcome and you can now include this in your design elements most often that's going to be rectangles so that's going to look something like this and soon enough we'll also add colors so that you can overlay elements on top of another to make complete designs but let me quickly explain how exactly we made the text elements work the way it works is that we simply set the selected shape R to text once that happens we're calling this create specific shape function and into it we pass the shape type in this case the shape type is the text so we call the create text function and this simply creates a new fabric text element gives it a position a fill font family and anything else you might want this is more fabric related but in case you want to dive deeper into how we create different elements like circles rectangles texts and more you can see it all within this shapes file even how we create lines circles and triangles with that said the only part we're missing right now are the images we want to be able to upload images to make your figma app stand out from all of the other whiteboarding tools available right now so how do we do that first we have to create yet another ref const image input ref is equal to use ref it's going to be of a type HTML 5 or rather HTML input element that at the start can be set to null there we go that's good this image input ref will be a reference to the input element that we'll use to upload an image to the canvas we want to upload the image automatically once we click on the image icon in the Navar so we're going to use this icon to trigger the event on the input without actually showing the input that's exciting right so to do it we can scroll up B down to where we have our switch statement where we have the reset delete and more in this case we want to add another case and and that's going to be image we want to upload an image to the canvas so first we want to trigger The Click event on the input element by calling the image input ref. current question mark. click so it's like you clicked on the form then we want to set the drawing mode to false so is drawing. current is equal to false because we're not drawing we're uploading the image and then you can check if fabric ref. current meaning if it exists then we want to disable the drawing mode while we're uploading the image so we can say fabric ref that current that is drawing mode is equal to false and finally we just break it now that we have this image input ref and we're doing something with it let's go ahead and pass it to our navbar because that's where we want to call it from image input ref alongside that we also want to pass an additional thing and that is how do we actually handle the image upload we can create a new function or a new Handler called handle image upload which is equal to a callback function where we get the event and then with that event first we stop propagation this prevents the default behavior of the input element and then we call the handle image upload coming from lib shapes to it we need to pass the file which which is e. target. files Z so the first file that we upload we want to pass the canvas which is just fabric ref like this as type any we want to pass the shape ref so we know what we're creating and sync shape in storage great now all of this is being passed to our Navar so let's go into it and here you can see that we are passing some information such as the image input ref and handle image upload into the shapes menu so let's dive further to see what shapes menu is doing with it if we scroll down you'll see that we have this little input right here that we will never actually be able to see but it is there it just has a class name of hidden but what's happening is that we're giving it the image input ref and then it gets clicked programmatically from our code once you click on the icon at top and it allows us to upload the image what happens then is that this handle image upload function from shapes is going to just get a new file reader it's going to load that image to our fabric canvas from the URL once uploaded we're going to set some basic width and height and then add it to the current canvas and finally syn that shape to our storage as we would with any other element pretty cool right so let's see if it actually works I will now clear our entire canvas and go to shapes and click image it then prompted me to select an image I selected one and then instead of an image I got an error in here it looks like I misspelled the target so hopefully you didn't do that it was just me but let's fix it there we go e Target files and let's give it another shot there we go image is there and it acts like a real element that you can move around the screen and even resize notice that if you pull it from the sides its aspect ratio changes but if you pull it from the corners then it just enlarges and remains in the same correct aspect ratio in this case I picked the illustration from our ultimate NEX js14 course if you haven't already definitely check it out if you're watching this video you are just the type of the developer that might appreciate diving a bit deeper into what nextjs offers we dive into details of using Tailwind figma typescript chat GP API even clerk for authentication and much more but with that said let's celebrate the fact that we can now successfully upload all different kinds of elements to our app including text as well so now we can connect the two and say something like ultimate next4 course and then we can even do some emoji right here all of this is coming along exceptionally well so you might be wondering hey what else do we have to do to improve this even further well first in what universe is the trash icon the image upload as well we definitely don't want that and maybe you even no it's the mistake and it's not doing that for you but I forgot a break yes it happens when doing if statements you don't have to have them but with switches especially if you're not returning earning you want to be careful about adding your brakes so now that's not going to happen the delete will delete and the upload will upload so with that said let's be serious what do we have to do next and to answer that question I opened up the live deployed version of our application with all the final changes it looks like somebody has been designing something which is always exciting and we can see that the primary difference from what we have in our work in progress and this one is the right sidebar uh the left sidebar is the layers navbar allows you to add and remove the elements while the right sidebar does a lot of exciting stuff it allows you to change the size of specific elements simply by typing numbers it also allows you to change different types of fonts so you can go with Times New Roman or even Comic Sans if you're that crazy you can also modify the colors of all the different elements like rectangles and even text elements and let's test it out for a rectangle there we go that looks good and we can also change the stroke to something like red there we go this is very crazy but you can do it that's the point you can do absolutely everything you want within this figma clone because it is yours not to mention that you can also export the Cannabis to PDF that looks something like this so we'll make all of this possible with our right sidebar so I moved back to our application that's currently working progress and I expanded it just barely so that we can see the right sidebar and now we can get started working on it to get started working on our sidebar we can open up the page and within the page we can see that we're using this right sidebar without passing any additional props to it let's navigate to it and ask it what it needs we can start from its jsx structure we can modify some Styles here since we just copied them from the left sidebar such as change the left zero to right zero and remove the Overflow y Auto and the padding bottom of 20 we won't be needing those now then we have the H3 that has a padding X padding top as well text extra small and uppercase that says design right below that H3 we can create another span element and here we can say something inspirational like make changes to Canvas as you like so we can literally make any changes right here let's also style this just a tiny bit by giving it a class name equal to text- XS for extra small text- primary D gr-300 margin top of three padding X of five border B for bottom border D primary d gray -200 and padding bottom of four there we go that's looking good I also believe I'm a bit zoomed out so I will keep it like that just so we can nicely see everything without taking too much space from our code or since we're just working on the right side I can even expand the code right now and we can still see the sidebar on the right side we can even expand it a bit so it looks a bit better there we go now we can see everything below this pan we'll render different components that modify different aspects of our elements such as a self-closing Dimensions component coming from settings Dimensions below Dimensions we can also render text which is going to be for text modifications which can be imported from settings text let's not forget the color modifications which is going to be coming from settings colors then we can duplicate the color because we have one more for changing the stroke color and finally we have one for exporting the canvas as a PDF so now if we save this you'll see I'm guessing a lot of Errors because some of the Imports might not be correct although for you there might I'm going to fix them on my end that's going to be coming from UI label we have to install these two packages from shaten from the text we have to fix the Imports here as well and we also have to install the sele component we'll do that soon let's just keep track of the shatan components we have to install that's select label input as well what else do we have let's see in the Coler we also get the label we already have added that one to the list and finally we have the export where we have to install the button which we already have and everything else is exactly as it should be so let's close all of these components and let's install all of the shadan components that we'll need to making our right sidebar possible I'm going to open up the terminal just bring us back to the last command which is going to be shaty nuui latest add and now I'm going to add a select a label and an input all at once by pressing enter we'll explore all of these components in detail once we actually dive into each one of these components now as soon as those get installed immediately on the right side you should see something that looks like this I even zoomed it in a bit so you can see it better we have design make changes to Canvas as you like the width the height the text the font size the font weight and the ability to choose different background and stroke colors keep in mind that the majority of these are not functional yet so if you just select a specific rectangle and then try to modify its values you'll see that it won't do anything similar like if you try to change the font you're going to get a lot of warnings and errors in this case and if you try to select a color you're going to also get a lot of Errors the reason why I'm showing this to you is to let you know that we will Implement all the functionalities for all of the features in this sidebar together what you can see right here is just the template just the skeleton just the UI that allows us to make all of this possible so let's get started with the first one on our list from top to bottom which is Dimensions that allows us to change the width and the height properties of our elements on the screen to make it work we'll have to go back to our beloved page. DSX and believe it or not add yet another ref to the list of our refs we can call it const is editing ref which is equal to the use ref and at the start we can make it null now we want to immediately use this rev in one of our listeners so right here below where we have all of our canvas.on we're going to add an additional one called canvas.on and we're going to call it selection specifically selection created this is when a user selects the screen there we get options which can be any for now and within it we can call the handle canvas selection created and to it we can pass the options the is editing ref which we just created as well as the set element attributes and of course we have to close this canvis properly so the app doesn't break now that we fixed that we can see that our handle canvas selection is complaining a bit about the is editing ref and set element attributes the fix the is editing gra part we have to see why the type is complaining so if we go to the handle canva selection created and then to its types right here by command clicking it we can notice that it's not even accepting a type so we can simply say is editing ref and that's going to be of a type react. mutable ref object of a type Boolean like this the reason why we're creating this is editing ref is so that we can know if the user is manually editing a specific element so now if we go back it's going to complain but now for a different reason that's because we have to set it as a Boolean not as a null so if I go at the top I can say fals here at the start instead of a null if we go here you'll notice that now that's fine it knows exactly what it is but now we have to create this new function set element attributes and that's going to be a Setter state so right here at the top we can create a new State field use state by using the used State snippet we can call it element attributes and set element attributes like this at the start equal to an empty object specifically an empty object with all of the empty properties like width set to an empty string height set to an empty string font size also is going to be sets to an empty string we can do a font family we can do font weight we can also do a fill which we can do something like a a BB CC I believe it's a gray color and then we can also do a stroke of that similar color and we can further explain the type it's going to be called type attributes there we go so now we can set custom ele attributes for each one of our elements and we're passing thats that are function into our listener on the selection created great now let's not forget we have to pass the is editing ref back to our right sidebar so let's go right here and let's expand our right sidebar and pass it The Element attributes which is the new state we have created as well the set element attributes we can also pass it the fabric ref because we might need it to know what we're doing with the canvas as well as that is editing ref equal to is editing ref we can also pass it the active object ref just so we know on which object we're currently working on as well as sync shap in storage so that we can nicely sync it after making the change like changing the color we want to make sure that that change is synced to storage with that said we're now passing all of the right properties to our right sidebar so we can go into it and we can pass the necessary ones to our Dimensions that will allow us to change the width and the height of our elements first we have to destructure the props we just passed so let's get them right here at the top we can get the element attributes we also can get the set element attributes let's get the fabric ref next we can get the active object ref let's also get is editing graph and finally the sync shape in storage Great don't forget we also have to Define the types for all of these so we can say that's going to be of a type right sidebar props being imported from types and in there we also need to add that is editing ref of a type react. mutable ref object of a type Boolean great so now we can use all of those props and before we use them let's figure out exactly what we need to pass to our Dimensions component it's the width the height and the handle input change now where do we get these from well we can get the width and the height from here element attributes that width and height is going to be element attributes. height now when it comes to the handle input change we can Define it right here within our right sidebar so we can say handle input change is equal to handle input change and we can Define it right here const handle input change is a function that accepts a property of a type string and a value of a type string as well and based off of it it can do something do you remember that is editing ref that we added just before well here we can check if not is editing ref. current meaning if the not is manual editing is set to true then we definitely want to set it to true so we can say is editing ref. current is set to true so once again let me explain what this means we're going to have the automatic editing which is this one where you drag and drop elements and move them around and scale them and do everything on the canvas but then this editing graph checks out if you're editing elements manually through the fields so then we need to turn it on to know how it should change those shapes once we know that we can then set element attributes which is the state function we have created get the previous attributes of that element and then immediately return something with this structure you wrap it in parentheses and then return an object we want to spread all of the previous elements and then just update the specific property to that property value that we're changing so for example if we're changing the color property will be color and value will be something like red finally once we have updated the state we also want to modify the shape and to it we need to pass the canvas which is going to be fabric ref. current as fabric this is a state and and right now it's going to complain that's because I haven't yet installed the types for our fabric so if you quickly notice any kind of imports we importing Fabric or anything like that you'll see that it currently cannot find a decoration file for the types of fabric so when using typescript some great packages allow you to install additional development dependencies like the types for those packages so in this case we can copy this command and paste it within a terminal to install the types for fabric and the second one is similar to this one but it's types uid that's for the package we have previously installed a lot of typescript documentation will be done automatically for us just for the reason that we're using these packages great so now here we can say fabric. canvas like this and now we don't even have to import this because we have installed that Dev package the second thing we have to pass is the property that we're actually modifying like the color or in this case the width and the height the value we want to change the property to the active object ref that we're currently modifying and then sync shape in storage so that we know what to sync and this is our handle input change that we are now passing to Dimensions so let's go into the dimensions and see exactly how it works it looks like that it also wants us to pass the is editing ref so while we're here it's easy to do that it's just is editing ref is equal to is editing graph and now it's no longer complaining so now if we go into it let's see exactly how it works we have two different dimension options label of w for width and label of H for height then we have a section with a div where we map over those options and for each of these two options we show a label which is this letter right here and then we also show the input this input is what you can see right here the most important thing is calling the input change which is exactly what we have done right now together with this handle input change function and then another important thing is calling the on blur functionality on blur Handler or listener is triggered once you exit out of the input so right now it's not blurred it's active but as soon as you click outside the on blur activates and that way we can know to stop the manual editing turn it to false and get back to automatic editing that's going to allow us to resize the elements like this great so with that in mind the dimensions file is completely done typescript is no longer complaining which means that it should be working so let's give it a spin I'm going to put this in the center and I will try to change the width of this image let me make it something like 500 there we go that's a bit stretched out and let me change this to 400 there we go that works that's great it's a bit tricky with these images because you cannot know how to keep the correct aspect ratio on different pixel sizes but what you can do is automatically resize it by holding the corners and then moving it like this that way it keeps its original aspect ratio but something is a bit weird right here you notice that the values remained exactly as they were 500 and 400 and only if we click on it they're going to update how can we make it so that they update in real time as we are resizing let me show you if we go back to the page and scroll down to where we have all of our listeners which is right here Mouse down Mouse move Mouse up object modified selection created and even resize below selection created we want to create a new one called canvas.on and here we want to look into the object scaling so once we scale the object as before we get the options we open up the function block and in this one we'll call the handle canvas object scaling and it we're going to pass the options as well as set element attributes why are we passing the set element attributes well that's because it will allow us to modify the width and the height of those elements so now if we collapse this and go back it looks like we have to properly close it by giving it one more parentheses and if we go back I'm going to bring it back to 500 and 400 and I will try to automatically resize it check this out it updates in real time and you know exactly where it will be so if I wanted to do 300 I can do exactly that and notice how it kept its aspect ratio great now modifying text elements is a bit different sure you can scale them up as you would with any other elements and you can also stretch them which you definitely don't want to do and that's exactly why for text elements we're going to focus on a completely new set of tools you'll be able to change the font family font size and the font F weight so to get started with that let's move to our right sidebar and let's focus on the text element similarly as to our Dimensions we have to pass the width and the height from the element attributes to our text we'll have to pass the font family and once again you might be wondering where will that come from well it's coming from the element attributes so we can say element attributes. font family similarly we want to get the font size and the font weight as well and the last thing is that handle input change which will be exactly the same as it was on the dimensions the element attributes store everything that element could have like Coler 2 we'll see that soon and then this handle input change the way we developed it is agnostic to which properties it is changing meaning that it can do colors texts as well as sizes so let's see exactly how text works it accepts all of these properties and then it renders a select field this select field we can find right here at the bottom and it's basically nothing more than a select that chooses which select to show is it a font size or is it a font weight once we choose it we have its content and the trigger to be able to choose from different options that's basically it now let's see what else do we have besides the select so we render the select we do it for the font size weight and family and pass the appropriate handle input change to it which then changes all of these properties within the attributes of that specific element so let's give it a spin so let's try to modify this text a bit by clicking on it and then choosing a different font like Times New Roman Comic Sans or the brush script there we go this one is really specific I'm going to keep it at helvetica then you can choose a font size we can go to something smaller like 28 or 36 finally you can also change the weight you'll notice that some fonts do not support different weights like helvetica in this case but you can go full bold on it and that works without any trouble that's basically it all there is to it to modifying your text Fields with that said let me bring this back and let's focus on the color color is interesting because by using Color we can modify both the text Fields as well as the rectangles and other shapes so let's pass all the necessary ones to our first Color Picker right here such as the input ref which corresponds to color input ref and this is a new ref which will create right within the right sidebar because this is the only place where we use it so we can say const color input ref is equal to use ref at the start equal to null and don't forget to import use ref as this is the first time we're using it in this file we can also duplicate this below and do another one for the stroke input ref stroke is like a border it happens outside of the element so for the first one we're passing the color input ref then we can pass the attribute that we wanted to change which in this case it is element attributes. fill we also want to modify the placeholder which is going to be the color and the handle input change which is equal to handle input change let's also not forget about the attribute type that has to be set to fill because this one is changing the fill and we can duplicate it right below the second one won't be changing the fill rather it will be changing the stroke so here we can say stroke element attributes. stroke placeholder also stroke and also this is going to be a stroke input ref there we go so now we have those two Pickers right here and they look great now let's see exactly how they behave if we go into here you'll notice that we're getting all of these fields and then we have an input property that allows us to choose or enter a specific color believe it or not this is a builtin HT ml 5 color input no additional packages the only thing you have to do to make it happen and to turn it from a regular input like this one right here is to add a type of color and you have a built-in RGB Color Picker great now if we select a specific element and if we try to modify the color you can see that it works like a charm let's try to go with this bluish variant that matches our design so I'm going to go right here and go for a bit of a Bluer one there we go that's more like it let's also try changing the stroke we can go to a lighter color there we go that's looking great I hope you can see it if you want to you can also implement the opacity for now I didn't go that far so I simply left it at 90% And this is it this is how you change the color and the stroke of a text field but what we can do is also modify the color and the stroke of a rectangle or any other shape so you can go here and you can select it and then modify its color like this and you can also modify the stroke there we go that's more like it pretty cool stuff right and it is so simple to make once you look into this I know you didn't code out this component but it's nothing more than a div with an H3 a couple of inputs and labels that's all that it is and we can also remove this 90% as it's just static code so this way we don't even see it great with that said the last thing we have to do right here is the export so let's go into the export component and see how it works the only thing that this does is it has a button with an onclick that it calls the utility function called export to BDF and the only thing that that function does is it takes the canvas by selecting it with a query selector then it turns it into a PNG image of the same size of the canvas width and height and then saves it at a PDF so let's test it out if we go right here and try to save this I'm going to click export to PDF and we get an error this error is saying that the doc is not defined so if we go back to utils we can see that the doc indeed is not defined and that's because before to show you how this truly works I commented out this part so let's bring it back back what powers this PDF download is the jspdf library which we can also import right here at the top coming from jspdf a simple Google search will point you to a jspdf mpm page where you can see that it has 1.1 million weekly downloads and basically the only thing it does is it prints a document so let's install it by running mpm install JS PDF after that this red squiggly line should be gone and with it we should now have access to this dock right here so if you go back and try to export to PDF this time it actually does it and if you open it up it's going to look something like this I didn't go as far as changing the background color of this document you could change it to match your canvas but already this is a good starting point for you to start adding more features if you want to with that said we can close all of these files and believe it or not we're completely done with the right sidebar so now let's expand this to see it in its full glory and on my other screen I'm just testing it as well by saying something like test right here sending some emojis or even collaborating live on the document so all of the stuff that we explored previously still works and since you have the same privileges as your friend you can now just delete those elements as as well and they get removed from the layers and the last thing we can focus on are live blocks comments they're currently in beta and they allow discussions on your whiteboard or or any kind of website effortlessly essentially you can just click it leave a comment and everybody can have a discussion on whether that feature or design is good or not it works within the text editor but in this case we're more interested in doing it within a whiteboard black interface or within a full figma redesign so you can do exactly what you need as it's flexible and you can attach it to a specific point on your canvas which is exactly what we'll do today so let's navigate over to live blocks comments documentation page and just start following the steps first we have to install it we have already to install the client and the react so we simply need to install the live blocks react comments going back to our code we can say mpm install and then add live blocks SL react DC comments the second step right here is to initialize the live blocks config which we have already done create the live blocks client which we have already done join a room that is done as well and then add live blocks room to our page we have done that and here we dive into adding comments they use the used threads hook to get the threads in the room and then use the thread component to render them finally they add a way to create threads by adding a composer so we can use their example right here by copying this entire piece of code within our application we can go to our components and then comments and create a new file called comments overlay. TSX within it we can paste what we copied of course make sure to fix the LI blocks config path which will be coming from add/ Li blocks config and then we need the composer and the thread now that we've done this let's see if there's something else we have to do within our docs and there is and that is adding the styles to our global. CSS so we can copy that collapse this and go to global. CSS and you'll notice that I already added this at the start for you so you don't have to worry about that at all with that said we can proceed with creating our comments and the first question is within where are we going to use this comments overlay it's going to be within a special component called comments so we can create a new component called comments. TSX inside of which you can run RFC we can make it into an export const not export default and within here you'll to use a special property coming from live blocks and that is client side suspense coming from Li blocks react this allows our comments to be rendered on the client's side and don't forget to pass it a fullback which for now we can simply set to null within this client suspense we can specify a new Dynamic block of code and then render the automatic Return of the comments or overlay that we have created just before and we can import it right here and now the question is where can we call this comments component well we're going to call it within our live component right here where we have the cursors and everything else we can simply show the comments because the comments themselves aren't anything they're an overlay that appears on top of different things so we can simply import them right here if you do that and go back to our app maybe just spread it up a bit you'll be able to see something that looks like this write a comment and you can start writing and then create it that's because the liveblock steam within our comment overlay already provided a composer but we want to hide that for now because we're going to show it only on a specific click once we actually know where we want to place. comment so let's figure out within where are we going to use that composer we already created a skeleton for one component called new thread that for now simply returns children nothing else and we briefly used it within our navbar if you remember if we select a comment then we trigger the new thread component where we have the button and then everything else within it but the new thread right now is completely empty so let's focus on the new thread so we can first add a thread and then we can focus on displaying them and if you came this far into the video then you already must be a pro so I went ahead and prepared the complete new thread component for you and documented it well with a lot of comments so in the read me down below you can find it and paste it right here you'll notice it's a lengthy one but the majority of the code right here is actually comments so I will suggest maybe pausing the video a bit and going over it and trying to understand exactly what it does but don't forget I'm here to go over it with you as well and before I go ahead and explain it you might notice that we also need two additional components for the new thread to work the pin composer and the new thread cursor so create a new file within comments called pend composer. TSX and you can find the code in the read me down below finally the last one is the new thread cursor TSX and you can also paste that right here now if you go back you'll notice that it's still complaining about the pin composer and let's see why it cannot get it it's pinned composer I think that's how we named it yep that looks good and if we look the export here export Bend composer that's looking good it looks like as soon as I removed it and just automatically imported it once again by clicking here even though it's the same same path it's no longer complaining and it works so now let me go ahead and explain exactly how this new thread works we start off with tracking whether we have placed or not placed the new comment yet so we have three different states placing placed or complete and it's PR cool that we can make some kind of an enum with typescript like this so we know exactly that this creating comment can be only one of those three states then we're using the use create thread hook to create a new thread coming directly from live blocks and here I left for you a documentation page it simply exposes the mutation to create a thread then this is a special function that I have created called use max Z index that Returns the highest Z index of all threads that way we can always show it on top we store it in a variable right here then we set state to track the coordinates of the composer which is the live blocks comment editor we also need to know where on the screen are we showing it we then set state to track the last pointer event we'll use this later on you'll see why we also need to set additional states to track if the user is allowed to use the composer such as only when the composer editor is actually opened and clicked if it's not opened and expanded there's no input that we can type in so that's why we need to track that state we also have the ref to connect it to that composer within this use effect we figure out if the composer is already placed and keep in mind composer again uh may be a complicated word but it's basically an Editor to create and edit comments so if it's already placed don't do anything otherwise once we click place the composer on the screen with this new comment function and if it's already placed click out de side to close it and if it's not placed once you first click on it it sets it down again it's all going to make much more sense once you see it in action as a matter of fact let me show you how that works on the finished website I'm going to go right here select a comment and click on it right here this window right here is called a composer and here you can do all sorts of things it has some simple styling capabilities like you can make text bolded you can also make it italic and so on which is pretty cool you can also add emojis and even mention some people by name of course if you implemented authentication into this app definitely something we could explore in the next one if you like this video so that's our composer so now let me proceed with explaining it further here we add an event listener so we can listen for clicks on that new comment and this change ches only when the creating comment State changes the Second Use effect is for dragging the composer so here we're tracking the pointer move and modifying the coordinates of the comment and this one just ensures that the comment is placed exactly where the cursor is you can see that by these listeners so we're looking for the pointer down or even for the context menu because from the context menu which we'll Implement soon you can also choose to add a comment now finally on composer submit is a function that creates thread and resets all the states now this part right here is very important if you just get the current X and Y positions it's going to get it relative to the entire window of the browser but keep in mind we're working within a tiny portion of that window which is the canvas we don't want the left side bar the right side barar or the nav bar bar to mess up with their positioning that's why we use the get bounding client wct which only gets the positioning from the div your within in this case our canvas and then we can properly calculate the position where we want to create that thread and this is the function that does it we pass it the body and the metadata which contains the X and Y positions resolve the set to false because nobody has yet seen it and we increase the zindex to + one we do some quick cleanup by setting the creating comment state to complete resetting coordinates and setting the allow Ed composer to false with that said we return this slot right here and Slot is used to wrap the children of the new thread component and allows us to add a click event listener to the children a bit of a complicated explanation but Rad's documentation Dives a bit deeper in simple words it merges its Pro stops onto its immediate child essentially the slot component is a utility component that doesn't render any D element itself but it clones it children and then feeds them with a prop that you pass into the slot that way you can attach different click handlers to children without knowing what kind of element they are very interesting component I know but essentially it attaches the on click and the style to whatever children we pass right here finally if we have the composer coordinates then we render the composer that looks something like this a portal is a way to render children into a Dom node that exists outside of the Dom hierarchy of the parent component so basically it stands on its own away from the navbar the way we typically use portals is to render components outside of their parent component in this case we want to be able to do comments anywhere where on the screen and this button right here is within the Navar but you're not going to leave a new comment within the Navar you're going to leave it somewhere else which is exactly what we portal out of the Navar into the canvas and then within it we display a pin composer which is nothing more than a simple div containing the common image and then the famous composer component coming directly from Li blocks allowing you to create a new cont comment and that my friends is the new thread component here you also have a new thread cursor which is basically once you place your current cursor on that comment it basically just displays a different version of the comment so you know you're hovering over it okay a lot of talking let's see what we have in our code if we now go back right here and maybe even make it a bit larger we can now click on this comment and would you look at that it appears like we're adding a comment let's let's try to click on this ultimate next4 and we can try writing something let's say make it ultimate next4 course and press enter with that a new comment will get added but it does seem like it kind of went away it's so hard to see it even if we zoom out it looks like it's not even on the screen so let's figure out what's up with that we have implemented the ability to add add the comments right now but if we go back to the comments overlay you can see how we are displaying them we're simply mapping over them but we have no idea where they will show up so let's just first consol log the comments to see if we really created them so if I go here and inspect within the console we can see that we have two comments right here and they're being reloaded quite often later on we'll memorize our component components and this issue will be fixed but the most important part for right now is that we have those two comments we created and we should be able to see the metadata Yep this is great we're storing the exact coordinates of where we want to place them and you can see the comments within that thread there we go now the reason why they're not showing up is quite obvious we are not attaching the X and Y axis to them that means that they have no idea where to go so to fix it what we have to do is create a modified version of the thread component provided by live blocks while still using it but call it a pinned thread that pinned thread will behave like a pin that you put on a board it's going to stay exactly where you created so the complete comments overlay code is in the read me down below so you can simply copy it and paste it here and you'll also notice that it uses this special pinned thread component as well so let's create it right here by creating a new pinned thread. TSX and you can also copy it from the readme and paste it right here now that you've done that let's go ahead and analyze what are we doing differently with our comments overlay we're still using the used threads hook which is the one that gives us access to these comments we introduce the max Z index to know where to display them we map over them and then we call the overlay thread instead of a simple thread provided by live blocks this overlay thread allows us to edit the metadata and we also get the loading once the user uses or consumes that thread we create a ref for it because we have to keep track of that element to be able to know where to position it we increase the Z index and add it to the metadata so that we know once we create a new comment it should go on top top of it and finally we return a div that renders the pin thread and the pin thread is nothing more than the original thread coming from live blocks but we have wrapped it with a couple of divs to make sure that it shows exactly where it should show on the screen on correct X and Y coordinates and would you look at that I have added two comments one while I was testing and another real one make it ultimate next4 course in the current version we cannot move these around that's something that you could explore implementing as well but let's go ahead and try to create another one maybe we can say something like change this color to purple and press enter it gets added but you can notice that it shifted just a tiny bit one thing we can do is also close these threads or resolve them just by clicking on them so so let's try to do one more for changing the color I'm going to place it here change color to purple and you can see it kind of moves a bit to the top left and you can also add the Emoji but it's going to appear after you reopen the comment we can look into that later and we also have to look into these emojis right here these are for reactions but when you're actually chatting if you try to select an emoji right now it doesn't do anything anything so that's something we also have to look into now most of the bugs that you've seen on my end right now shouldn't have existed at all for you for you everything should have worked the first time that's because I just recently updated the new thread component which I provided to you at the start and in there I added a couple of these fixes for the positioning of the thread as well as closing and opening it so if we go to line 175 and 6 you you can notice that I just made a change to look into the composer chords which are relative to our current canvas so that's a much more precise position and also if we go right here you'll notice that we're now properly closing and opening the composer if we click on it and the other file where we made some changes is within the pin thread which you also had access to specifically right here we added an if statement checking if we click on a specific icon such as emojis or anything else then we exit out of the function and not minimize the window that's why we weren't able to select emojis before so now if we try to add a new comment with those changes right here on the orange part and I say something like test and press enter you can see that it still brings it a bit up let's figure out why that is also we have to fix those scroll bars on right side and at the bottom we'll do that very soon but first let's fix the positioning I believe we can fix that by modifying ing the classes within the live component these ones right here we are never setting the relative positioning and that is very important we have to say relative we can give it a full height by saying h- full instead of 100 VH we also can give it a flex we can also give it a flex one so it expands the full width however much it can we are also goinging it a full width item Center justify Center and we don't need text Center that was before when we were just playing with the lightblocks features so now if I remove the text Center and save it and relo the canvas you can see that now the comment is exactly where we placed it that is looking good usually if you had real users here you would be able to see their profile photo and also their name the way you close the comment is you just click on it great and as soon as we Implement these class names you can also see that no longer can we see those scroll barss to the right so that has been fixed as well now let's look into the Emoji issues so for example I'm going to say something like let's do this one right here monco DB is great by the way notice that we still have those bars I was wrong we'll fix them so I'm going to press enter we have our comment Anonymous and let's try to emote to it so we can put a reaction which when you reopen you can see the reaction here here and you can also reply and say something like yep it is and you can also tag people if you had real users and you can also add emojis to comments as well and press enter all of that works in real time and of course if another user had this opened they would be able to see it too let's try to replicate the situation where we have those scroll bars I think if we do this Yep they're going to appear and now we can navigate to global. CSS and we can try to Target the body element and simply do something like overflow and then hidden because we cannot scroll through our canvas and as soon as we do that the issue is fixed but of course many of these issues that we're experiencing are here only because we're working on such a small screen but as soon as we expand it you can see the right sidebar and it looks so much better and of course you're not going to have many of these issues that we experienced but hey it's good that we handled the edge cases do so with that in mind the comments work we can say great we can add emojis we can do all sorts of different things and they're actually being placed exactly where we need them so with that said you have successfully implemented the ability to add all kinds of different elements text elements as well delete them reset them add overlay comments keep track of the layer history as well as modify everything there is to modify about elements like they're colors Strokes font families and the width and the height so what do we have to do next the next thing we can focus on is the menu now the menu right here when I click it allows me to save the image copy help or inspect but on the deployed version once I click I have these special options that I can do for example even if I don't know that I can press the forward slash to chat I can rightclick and then see all the options like chat undo redo and even reactions so let's go ahead and Implement that using the shaten menu specifically we'll be using shat 's context menu it allows us to display a menu to the user such as a set of actions or functions triggered by a button exactly what we need the installation is simple we just have to run MPX chat CN UI latest add context menu so let's simply paste it to one of our empty terminals it's going to install it and then here is the usage we can first import all of the Imports necessary to make the menu we can go back to live and just use them right at the top of this page then we can copy its usage and we can scroll all the way down to the jsx portion of our live. TSX the only thing we have to do is wrap everything we have into a context menu so we can do it right here what will temporarily break our application but we'll soon fix it so in this case we have the context menu which needs to wrap absolutely everything so let's first copy the rest and keep the context menu on top and of course we need to close it right here at the end that is the primary wrapper for our menu the second thing we need to do is say where the context menu trigger will be and in this case our entire div or the C canvas is actually a context menu trigger so we can replace this div to context menu trigger like this and of course we have to close that context menu trigger at the bottom there we go and the next thing we have to do is scroll all the way down that's going to be right here and we want to close the context menu trigger and then after it still within the context menu render the context menu content like this we can give it a class name equal to write- men- content and I think this is enough for us to get back to our application there we go not much to see but we can start playing with it if you right click you'll be able to see an empty menu so now let's start adding the menu content right here we can map over our shortcuts coming from constant by saying shortcuts. map where we get each individual shortcut item and then for each one we return a context menu item like so within the context menu item we can simply return a P tag that's going to render the item. name and of course each item in a map has to have a key so we can say item. key will be the key great now let's first dive into the shortcuts to see what they are this is simply an array of predefined objects one is to chat two is to undo three redo and four reactions so now if you go here and right click you can see all of these elements on which you'll soon be able to click to activate them and we can also render the shortcut right here by rendering A P tag with a class name equal to text- XS text- primary DG gr-300 and there we can render the item. shortcut if you save it and right click you can now see some reactions of course it's not so well styled and right now you can just do that reaction by clicking it but you cannot yet click on the actual item to activate it so to make that happen we can give the context menu item an onclick property which will handle the context menu click and then to it we can pass the item. name of course this is the function that we are yet to create so if we scroll a bit up we can create a new const handle context menu click that will be a use callback hook coming from react and it will accept a key of an action that we want to click of a type string and then we can return the actual function since we're using the used callback we also need to pass the dependency array the used callback is used when you want to memorize the version of the Callback that only changes if one of the inputs has changed so if you click on one of these items for the first time for example the chat it will call this function but the second time that you call it the same one it's not going to recompute the values because the input hasn't changed so we're just making our app more optimized right here we can call a switch statement that's going to look into the key and then the case is chat with a Capital C then we want to call the set cursor State and to it we can pass has the mode cursor chat. chat previous message is null and message is equal to an empty string and then we want to break it now let's test it out if I go back we'll see too many renders I think that's because if we scroll down you'll notice that I immediately use this function and it's even complaining right here you never want a call function like this because as the code is being interpreted it's going to start reading on click and then it will start reading handle context menu click and hey this is a function call so let me call this function immediately and not on the onclick so to remove this error we simply have to put it in a callback function which is going to look something like this open parenthesis and then a function call now we won't have too many rerenders it's only going to call it once we click there we go now let's go back and let's add other pieces off or switch should I say the second one will be case undo again make sure to use a capital first letter in that case we simply want to call the undo function which is coming from live Block's hook remember we used it before as well so just so we don't have to recall the hook we can also just pass it into the live as a prop so if we scroll all the way up you'll notice that we're not passing those as props right now let's see where we're calling the live component we're calling it within the page and you can see that we're not passing anything to it but what we can pass is the undo equal to undo as well as redo is equal to redo now if we go back to live we can tell our props that we are expecting it so we can say undo and redo and to the props we can say that undo is of a type function that returns void meaning nothing and redo is also a function that returns void now if we go back to our switch statement we can say undo call the undo function and don't forget to break it because we're not returning and of course on case redo we do redo and then break and that's about it now finally let's just style this context menu item by giving it a class name equal to write- men menu Dash item save it and now if we right click you'll see that it looks great you can immediately stop the right menu by clicking out and use the keyboard shortcut for example forward slash or you can also just click on it to activate it so for example let's say that you like to use your mouse more than your keyboard and you want to create a new element oh for some reason it doesn't want to create any elements on my end let me reload the canvas oh there we go now it's good so let's say you want to create a rectangle and then let's say that you change the color of this rectangle to something like blue as well and maybe bring it back to green but then say hey I like the blue one better it matched her design more so then you go here and click undo and you can also bring it back to green great also there's reactions which don't seem to open right now but when I click the letter e they do do open so let's see what that's about I think I forgot to add the switch case for reactions that's why it's not working so right here we can say case is reactions make sure to spell it exactly like this and then we can set the cursor state to mode cursor mode. reaction selector and then break it now if we right click and click reactions they show up great that is it for the menu that part is working now as well and as we're approaching the end of this amazing build let's try to remember if we still have some little bugs or some issues that we have yet to fix I think one of these is our free drawing although it technically works it's not being stored or saved anywhere and it will not show on other person's screen why is that well you can create it you can move it around but you get errors you Cannot drop it and you cannot even delete it and if you reload the page you'll notice that it's no longer there so let's fix our app to also work with free form drawing to fix our free form we have to go back to our page. TSX and there we have to add an additional listener we have to add a canvas.on path created so let's duplicate this listener right here below collapse it and just modify it to path created then we can call a special function handle path created coming from lib canvas so make sure to import it and let's see what do we need to pass to it we need to pass the options as well as sync shape in storage and the only thing it does is it gets the path object and then sets unique ID to that path and then syncs it in storage it is as simp simple as that so let's pass sync shape in storage and with that we should be good so let's give it a go I'm going to go to our shapes choose free drawing and try to draw something right here on our canvas there we go this seems like a Picasso so it should be good now if I select it and if I try moving it around that also works and the main question is will it sync into other person's device so the first way to test T it is let's reload our screen and it's there and the second way to test it is to check it out on another browser so if I open up free form and continue drawing right here you can see that it actually works but it waits until the shape is completed there we go that's interesting now we can also delete all of the individual shapes so if we click right here we can delete them there we go so as we're wrapping up I remember that I promised that we're going to fix a couple of typescript any types uh to make it a real proper typescript application so let's start from our live. TSX file right here at the top we don't have to import the type for the reaction event what we can do instead is just simply remove it from where we used it reaction event and just remove it it should be automatically understood by live blocks but you can see that these errors are still there property value does not exist on type room event well another thing we can do here is where we use this cursor we also said as any we don't want to do that we can simply remove it and make it so that Li blocks automatically defines the types but for this to work we also have to go into our types so let's navigate to types. TS and let's see where we're modifying the presentence so here because I wanted to make some quick fixes I put any but we don't don't really need that we don't need to import presents or even use it within a live cursor props all of that will work automatically by live blocks automatic types so now to make our cursor aware of its existence on the presence we need to go to live blocks [Music] config.txt type presence and we can say cursor X and Y coordinates and we can also make it have a message this way you craft your own types like this so now that should help a bit and right in here as well we can import that reaction import reaction event that we had from types and then we can also export it right from the LI blo config type room event is equal to reaction event there we go and now if you go back you can see that it's no longer complaining and we know exactly what this cursor is sometimes you have to go through a bit of docs to figure out exactly how to use typescript types for a specific package it's going to save you in the long run also I noticed we're doing some props drilling here by calling the use others on top of live but we're not using it anywhere within the live so let's simply delete it from here delete the import as well and then delete where we're passing it into the other component we can simply call it within the live cursors it's much cleaner so right here instead of just passing the others through props what we can do is just import and use others so const others is equal to use others coming from live blocks config and I don't believe we have to put it within curly braces so that's now good also this message could potentially be n now so we can say or empty string which will fix this error tab script is a beautiful Beast once you learn how to tame it but if you don't it's just going to annoy you so it's better to invest some time beforehand and then use all of the benefits that it provides and stands by your side so with that said we've made our app even better by cleaning some of the typescript anyes and check this out if you're having some issues inspecting the collaborative experiences you're implementing there's also this crazy developer extension that you can add to your browser there's a cool video that shows you exactly how you can use it but in a nutshell it gets added directly to your browser developer tools and you can monitor all the collaborative experiences instead of just the components console logs and elements pretty cool stuff so you can quickly install it by going to adding to Chrome just add it to your browser and once you do that it will be directly within your developer tools so if you open it up you'll be able to see live blocks right here and here you can see all of the canvas object in this case that is a live map so what we can do is I'm going to clear our terminal and you can see that the live map is completely empty you can also see inside of which room you are and what is in the storage you can track the presents cursor cursor Coler and more so what would happen if another user joins I'm going to put this to the side and bring another user in and immediately you can see in developer tools that now we can see two other people we can see me and we can see other that works very well you can also track their cursor positions cursor colors and more and if we add an element right here such as rectangle you can see that it also immediately appears under canvas objects incredible way to just Deb bug what's happening track events presentence and just in general improve our collaborative experiences now with that said let's get our app live on the internet deploying it won't be as easy as putting it on versel there's one extra step we have to do and that's because nextjs will try to render our canvas on the server side which can never work canvas has to work within the browser so for that we have to skip s SSR and the way to do it is to just wrap everything in a component C and then set SSR option to false so let me show you how to do that we can First Take absolutely everything we have within our page. TSX so simply select everything by saying Commander control a and then Commander control C to copy it delete everything from here yeah it's carry I know but just do it and create a new app. TSX file within the app and then paste everything there once you do that we'll do a little trick within the page. TSX there we'll copy What nextjs suggests us to do and that is Con app is equal Dynamic imported from next Dynamic and then callback function with the import off in this case are slapp and then we simply turn the SSR to false this is how you do it now our entire application will be rendered on the client side and we can simply say export default app so let's give it a spin if we go back everything works exactly like it did before but now once we deploy it we won't have any issues with the client side serice side rendering with that in mind let's create a new GitHub repo and push our code to it you can go to to github.com slne and create a new repo with a name of something like live blocks figma clone I think that will be good and you can create a repo then simply follow the commands by opening the terminal stopping it from running we can also delete one and say get in it get ad dot get commit DM initial commit then you want to modify the branch to main add a remote origin and finally push you origin main if you do that and reload you'll be able to see all of your code you worked so hard on right here on a public GitHub repo after that you can go to ver.com Just create an account or log in and you'll be able to see all of your projects on the dashboard we have many project so far most of these belong to some of our YouTube videos like this one which will come soon in this 3D portfolio you've seen some time ago we definitely need to do another 3D project there's also many projects belonging to our master class which is the official JavaScript Mastery boot camp program that helps you advance to Mid and Senior positions so if you're interested in joining just go to jm.pro and check it out with that in mind look at this there's 's a little issue right here with the Overflow on the vel's card we should definitely let them know about that with that in mind we can click add new add a new project and versel should be smart enough to automatically see your new project and click Import in this case we do have one environment variable so go back to your code go to env. local copy everything and simply paste it right here that's going to add the next public live blocks public key and click deploy this process usually takes about a minute so let's give it some time and I'll be right back and of course we cannot expect that the deployment process of such a long complicated and exciting project will just work so we do have a build error right here and it looks like it's pointing to the canvas node specifically referencing the loaders belong belonging to webpack it looks like we need an appropriate loader to handle this file type so going back to our code we can navigate to next. config.js and we need to add a proper weback configuration to render canvas we can do that by at the top saying webpack getting its config from the Callback function and then specifying utf-8 D validate of C commonjs space utf-8 DV validate there we go we also need something known as a buffer util which is going to be a commonjs buffer util let's make sure to properly enclose this in double quoted strings and finally we also need a canvas of a type commonjs canvas now we need to not just make this an object or a function block we need to return an object so let's wrap this in in parenthesis there we go and then now it will look good without any errors and finally return this modified config of course we're just returning the same config right here but what we need to do instead is say config externals push and then we do what we have done here so I've made a mistake we don't want to enclose it here rather we enclose it right here within this push so so that's going to look like this you close it and once we update the externals of the config then we return it and now our application should be capable of rendering a canvas and just in case we have some tab script errors right here below images we can say tab script and we can add ignore build errors which is going to be said to true just in case we have a tiny error so it still lets us pass with that in mind you can say get add dogit commit dasm and say modify config and then get push this will try to automatically redeploy our project so no need to try it manually again just go back to your projects open up the project go to deployments and you'll see that it's automatically building it and as you can see our website has been deployed you can go to project and then click visit if you reload you'll see a great loading which is almost instantaneous it just works right up the bat and you can start creating additional elements and layouts as if you were in a real figma I'm not a great designer but yeah you can definitely do some great stuff here you can also see that this collaboration Works immediately because there's another cursor here although it looks like they're away from keyboard there's a third one right here as well looks like somebody leaked my deployed URL so let's see if it speaks hi there stranger no looks like it's a bit shy doesn't matter this is it our live blocks application is now fully complete everything works from resizing in real time modifying the elements changing the colors as well everything works so seamlessly and you can see it just work you can also export to PDF which is great you can create additional elements all of that works in real time and and all of that works within your thig pro application with that said huge thanks to Li blocks not only for sponsoring this video but for building such a phenomenal piece of software that allows us to create these collaborative experiences and if you reach the end of this video you truly are the perfect fit for our ultimate next gs14 course the course that you just watched building this canvas it's great and it does use NEX GS but it does doesn't really dive into depth about all of these cool nextjs features that make it what it is such as the hydration error resolving caching Edge versus node runtimes Global State Management client versus server data fetching and just ensuring that your app works properly we do that within this course so we have deep dive with very detailed lessons build and deploy a complex app and even active lessons within which you can test your knowledge not to mention that you'll build one of the best developer apps out there which is a modern stack Overflow clone with that said thank you so much for being with me of building our figma clone application and have a wonderful day