Build and Deploy a Full Stack MERN Next.js 14 Threads App | React, Next JS, TypeScript, MongoDB

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi there and Welcome to our thrilling new full stack course where we'll take you on an exciting journey to build and deploy a powerful application threats this app gained over 100 million signups in less than five days surpassing Chad GPT and Tick Tock making it the fastest growing app ever so get ready to clone threads and then go beyond and add even more exciting features that even the original app doesn't have starting with complete authentication using clerk you'll enable email password and social logins like Google and GitHub as well as the entire profile management this leads us straight to the user onboarding process and immediately after a visually appealing homepage showcasing all the latest threads after getting inspired by other people's thoughts you can create your own even better thread or maybe instead of creating your own threads you prefer to let people know they're wrong by commenting on existing threads oh and a special multi-level common system will allow you to tell even more people they're wrong you'll also build a user search with pagination making it easy to find other users an activity page that displays notifications when someone comments on your thread and a profile page for users to showcase the information and modify their profile settings okay okay all of that is great but now I wanna introduce you to communities create new communities and invite others by using a customizable template email use clerks user-friendly interface to manage community members allowing you to change their roles from admin to member or even kick them out and once you're on admin you can also create threads specifically for your communities there's also a community search feature with pagination that allows users to find and explore different communities as well as Community profiles displaying their threads and members all while transforming a figma design into a fully functional application with Pixel Perfect and responsive design providing an optimal user experience on all devices with nextgs's latest and greatest features experience blazing fast performance and instantaneous page switching through these incredible features you'll get the chance to master many amazing Technologies including nexgs 13 with server-side rendering mongodb handling complex schemas and data population that goes multiple levels deep create beautiful layouts using Tailwind CSS and upgraded by using native Shad CN components Implement file uploads with upload thing and even real-time event listening with web hooks understand how middleware API actions and authorization works and explore and integrate new nexgs layout route groups there's also going to be data validation with Zod managing forms with react hook form creating reusable components solid application architecture and so much more but the main star of the show is clerk as you've seen in the demo clerk is going to allow us to instantaneously spin up incredibly powerful authentication features such as login and registration using email password and multiple amazing social sign ins SMS verification entire profile management and much more isn't this fantastic you now have a golden opportunity to showcase your skills to potential employers or clients with this incredible project so grab a cup of coffee and let's dive into this journey together is what I would normally say but I have a special announcement to make I will soon launch an in-depth full stack next.js13 course in which you will learn everything there is to know about next 13.4 and further my team and I have been working on this JSM Pro course for months and I can't wait to give you the opportunity to access it and truly Master the most popular web development framework of 2023 so click the link in the description if you want me to notify you about it and when you sign up I'll also send you the first part of our premium nexgs 13 eBook this is a paid ebook which you guys adore and this video is your last chance to get the first six chapters for free and with that said let's get started [Music] let's get started with our phenomenal threads application right from the beginning we can start by creating a new empty folder on our desktop and let's call it simply threads once you create it you can drag and drop that empty folder right within your Mt Visual Studio code window expand it go to view terminal and just like that we are ready to get started let's start by initializing a new next.js13 application by running MPX create Dash next Dash app at latest and then dot slash to create it in the current repository this is going to ask you whether you want to install the create next app to which we can say why and then it's going to give you a questionnaire asking you how you would like your app to look like in this case we can choose yes for typescript we can choose no for eslint we can choose yes for Tailwind no for Source directory of course yes for app router to get all of the latest and greatest of nexgs 13 features and we don't want to customize the default import Alias and just like that it's going to install all the necessary dependencies for us that's going to include react next typescript additional types and even it's going to set up Tailwind once our default packages have been installed we can install additional ones the ones we'll need to turn our app into a success so let's run npm install and the first package we're going to install is going to be add clerk forward slash next JS clerk is going to allow us to implement super simple but also super robust authentication to our application it does everything from social science email and password login to managing your account once you're in and there's a cherry on top we'll use it to its full potential by implementing its organization feature for our communities the next package we're going to install is going to be add upload thing forward slash react we're going to use it to upload our profile images next we're going to install mongoose we're going to also install a package called svix which we're going to use for web Hooks and finally upload thing itself right here this was just the react package for it and believe it or not even though this is a huge app these are all the packages we're gonna install later on we're gonna also install specific Shad CN components but other than that this is it everything else is going to be coded 100 by you so let's wait until this is installed and then we can set up our basic file and folder structure there we go we can lower our terminal just a bit and we can start exploring the code base we have the app folder inside of which our entire nextges app will be we have the favicon the globals the layout and the page so let's start by running the application by running npm run Dev to see everything that we have live on the website immediately on the home page we have this great nextgs template created for us by versel but of course we want to fully modify it so let's start from page.tsx by removing everything within the main folder and when I say everything I really do mean everything we're just going to keep a simple Main that's going to say threads or it can be an H1 saying threads right here there we go we don't need to import the image at the top so this is just a simple export default function home next we have the globals.css here next.js already provided us with the import of Tailwind as well as some default Styles so now we're going to override the globals.css in the description down below you're going to find a GitHub gist containing the full globals.css so simply copy it and then paste it over here these are just going to be some simple class names that are going to make the process of our development a bit simpler it allows us to immediately apply multiple Tailwind class names so we don't have really long Tailwind class lines you can notice that this also immediately gave us an error in the terminal saying that the BG dark 2 does not exist if it's a custom class we have to Define it within Tailwind so this is the right Next Step to move over to the tailwind.config yes and in that same GitHub gist you're going to find the modified tailwind.config.js file here we're going to have just some additional Styles colors and the general font sizes all of this is taken directly from figma which we're going to explore really soon and that is it everything else is going to be coded entirely by you so now we can stop the application from running by pressing Ctrl C and then Y and then re-run it once again to ensure that all of the changes have been taken we can then revisit localhost 3000 and you'll be able to see just one more error saying that we cannot find module Tailwind animate to bring our app to life even more we're gonna use some Tailwinds animate properties for which we have to install a special package so let's do that right away by opening a new terminal window we can put it side by side by our current one and right in there we can run npm install Tailwind CSS Dash animate now if we save it go back to localhost 3000 we should be able to see threads on top left now let's improve our file and folder structure to ensure that it is scalable so that we can keep working on it no matter how much our app grows in this case we're going to use Advanced nextgs13 routing properties we're going to start by using something known as route groups so in the app directory nested folders are normally mapped to URL paths as you know it if you create a new folder as forward slash auth it's actually going to add the auth route to your application however if you want to prevent the folder from being included in the routes URL path you can use something known as route groups they are useful for organizing routes into groups for example by intent or team and they enable you to have nested layouts we're going to utilize both of these two reasons for using route groups so the convention is simply to wrap the folder name within parentheses and here's an exam sample if you have marketing you can have the marketing folder and then it's not going to have the forward slash marketing forward slash about and then forward slash marketing for slash blog instead the routes are only going to be what is within it and it's not going to mention the outside folder so how are we going to use it in our application well we're going to first create a new folder which is a group route which is going to be called auth and inside of it we're going to have all the auth routes and the other one is going to be called root and inside of root we're going to have all the other routes of our application so we can move our current page within the root group and we can also move its current layout now if we go to layout and we fix the import for the globals.css because now it's going to be dot dot slash globals.css and then go back the app should be working normally which allows us to start adding additional routes within the auth folder because that's what we're going to start with since we're building a social media application we need to have our users signed in to know who can create posts so let's create a new folder within the auth folder and let's call it sign in at the same time this is also going to be a route let's create another folder called sign up now we have two folders within the auth folder and we can create a third one which is going to be onboarding so on top the sign in the sign up I'm also going to teach you how we can have that onboarding so the user can enter additional information about themselves and remember I told you we're going to use both of these reasons for creating nested routes so so far we've used the reason for organizing routes into groups but also that's going to allow us to use nested layouts so what we can do is we can create a new file called layout dot TSX and that's going to allow us to specify different rules for the authentication route for example we don't want to show the nav bar or the footer here so we can simply declare it that way so let's get started with the layout of our authentication folder we can start with a part that many people discredit or don't do at all which is search engine optimization or SEO I want to teach you how nextgs allowed you to have a much cleaner and much simpler but also more effective SEO incredibly quickly the only thing you have to do is you have to say export const metadata is equal to an object and you can immediately specify the title here which is going to be the name of our app as well as a description and in the description we can say something like a next dodgy s13 meta threads application immediately after we can export default function root layout and it's going to have some props inside of it which is going to be children in most cases layouts are going to have children because we need to display something within it also we have to define the type of these children because we're using typescript so we can say the type of this object is going to be an object where the children is equal to react dot node that's going to look like this inside of there we can return another way of writing this is to just put this object in a new line like so and then say that is equal to children and then put this on a new line as well so like this everything is just a bit more readable you can see the props and you can see the types of the props that we have and then the return of that layout now a really important thing is that we're going to wrap this layout with something known as a clerk provider this is what's going to allow us to use all of the clerk's functionalities so let's nicely wrap this within parentheses just so it's a bit more cleaner and more readable and this clerk provider is coming from add clerk forward slash nextgs so you can simply double click on that and press Ctrl space or command space on Mac and then click right here and that's going to automatically import the clerk provider for you if the Auto Import didn't work for you you can simply follow my imports right here saying import clear Provider from adclerk forward slash nextgs within that clear provider we're going to return an HTML page that's going to have a lang attribute equal to en as in English and immediately within it we're going to have a body tag that's going to have a class name equal to and now here we want to render a font and the way you do fonts in nextgs is you define it like so const enter which is the font we're using is equal to and now you want to import that font from Google fonts so we can say enter all caps and we can immediately import it coming from next font Google we're going to call it as a function and say subsets and then get the Aladdin subset this is all that we need and the subsets of course has to be an array because we can have multiple subsets within it and now within the class names we can simply say enter dot class name and this is going to immediately apply this font across all of our files immediately within here we want to render our children which is everything else that's going to be shown within this layout we also want to give it another class name so we can turn this into a template string where the inter class name is going to be a variable and after that we're going to have a BG Dash dark dash 1 to make everything a dark color so now if we save this we are looking good let's also import the globals.css here so import dot slash globals.css and let's save the file now if you go back to your website you're going to notice that nothing was applied the title is still create next app and the background is still white that's because this layout is only applied for the routes within the auth subgroup so for the onboarding sign in or to sign up so let's try to go to one of these routes we can do that by going to the URL bar and typing sign in we can check this layout in action if we create a new page.tsx file within the onboarding route and there we can simply say async function page and we can immediately just return something so we can say return and let's do a main which is going to have an H1 with a class name equal to head text and it's going to say on boarding and finally to be able to see it we need to say export default page now on our website we can go to forward slash onboarding and you can see that we're going to get a meaningful error from clerk saying that we're missing a publishable key which means that now is the right time since we set up our clerk provider to also create an account on Clerk and start setting up our authentication to do that we can go to the clerk's website by clicking the link down in the description and click Start building for free there you can sign into clerk using their own provider which is a pretty cool thing I'm going to continue with Google and now you'll be able to configure your first application by clicking add application and there we go you can enter the application name such as threads and you can choose how will you let your users to sign in for now we're going to choose email username Google as well as since we're developers GitHub but as you can see there are a lot of different options you can choose from as a matter of fact I don't think I've seen this many options for the sign in ever so it's pretty cool that I allow you to connect with whatever software you're using and let's click create application this immediately says that the sign in is ready and it gives us a quick start with xjs so we can simply copy this API keys by saying copy and we can create a new file in the root of our directory called.env.local and we can paste them there of course the keys for you are going to be different and while we're here let's quickly explore Clerk's dashboard we also have the users tab where you can see all of the users that are registered to your application and finally we have the organizations tab which we definitely want to enable because we're going to use it to create our communities so let's click enable organizations and let's toggle this on and click apply changes of course in this video I'm going to teach you about everything you need to know to use clerk on all of your applications but as with any software I would recommend you to check out the docs it's always useful to learn about how to use it properly so the first step we've already done which is to install add cleric forward slash nextgs then set up the environment Keys which already we have done by putting them in the dot env.local we have already mounted the clerk provider so that part is done as well and then the next step is to protect our application we can do that by creating a new middleware.ts file in the root of our folder so let's do just that and let's copy their starting code so in the root of our folder we can create a new file called middleware dot TS and we can paste their middleware starter in our case we also want to add some additional details to the configuration such as public routes which are going to be an array of just forward slash which is going to be public as well as forward slash API forward slash webhook forward slash clerk we're going to use this later on to enable the webhook functionalities for our organizations and then we can have ignored routes so these are going to be the routes that are ignored by clerk that's going to be forward slash API forward slash web hook forward slash clerk as well there we go and we can add a comma here this is the initial setup that we need to do and later on we'll be able to further modify the middleware Page by reading more from the middleware documentation where we can immediately redirect users to auth Pages or redirect them somewhere else after they've been authenticated so now let's check our next step which is to build your own sign in and sign up Pages clerk offers a set of pre-built components that you can use to embed sign in sign up and other user management functions so what do we need to do well we need to create a new folder that looks like this square bracket square bracket DOT sign up and then the page within it and then immediately we can import the sign up component from clerk so let's copy this go back to your app close all the currently open files go to app auth sign in and then within it we can create a new folder square bracket square bracket DOT sign in we close it and then within that folder we create a new file called page.dsx and there we can paste this sign in in this case and return the sign in and we can repeat this process for the sign up as well so let's go to the sign up new folder sign up and of course we have to do the dot dot and then create a new page.tsx and paste will be copied which is just the import of the sign up and then return sign up as a self-closing component and the next step here is to update the environment variables so we can specify the route of where we want to go after sign in or sign up so we can add that to the dnv.local right here in this case I believe after sign in we want to go to the onboarding so I'm going to add that right there and the last step is to embed the user button that is going to allow you to manage your full account information so let's simply copy this close all of the currently open files and go to our root page.tsx because this is what happens once we're actually logged in and we can override this by importing our user button and then rendering it right here now we've done a lot of changes so let's go ahead and test it out and see if everything works of course we're going to Center this in the middle of the screen later on but for now let's go ahead and see if our authentication works by choosing to sign in or sign up with any of these methods I'm gonna proceed with Google and immediately it requests us to add the username as well I'm gonna do JS Mastery and click continue and we've been redirected to localhost 3000 and you can see a little profile icon on top left if you click it it's going to open up a model that's going to allow you to sign out or manage your account and within manage account you can immediately change or remove your profile pic you can also change your username change your email choose which account are you connected with and even manage your password and active devices all of this comes right out of the box by using clerk if you think about it these are the features that we need in every single application but implementing them one by one could take hours if not even days to implement it not to mention that we have to be really careful about security so a task that would take you weeks to implement properly with connected accounts changing usernames passwords and more took just a couple of minutes using Clerk and that's why I'm so happy that to a phenomenal app such as threads we can leverage all of the clerk's functionalities right out of the box great with that said we're already signed in which I cannot really believe to be fully honest that it took just a couple of minutes but that essentially means that we can get started creating the layout and the structure of our home page so let's get started to get started working on our home page we can close our auth and go within the root folder and then page.tsx here we just have our user button and the layout but we can focus on the home page itself or at least its General structure so let's get started with removing this user button at least for now we're going to bring it on later but for now let's simply create an H1 here that's going to say home and we can give it a class name that's going to be head Dash text as well as text Dash left we can also wrap this in just a react fragment as we don't have to give it any classes now if we save this it's going to appear like you cannot see anything on your home page but if you hold and drag you can see that the home indeed is hiding there but it is white text on white background so let's change that and the way in which we're gonna change that is not going to be just on the home page because we want to ensure that that background remains on all other pages and that's exactly what the next js13 layout is for so let's go into the layout and let's start creating the structure of our app we already have the HTML and we have the body but now we're not just going to return children within the body we're gonna add some other things first of all we want to add a top bar so let's create a component with a self-closing top bar as well as a bottom bar usually it's a footer in this case it's going to be a functional Bottom bar and we're going to make it even more unique by within that top bar and Bottom bar we're going to create a new main section that main section is going to have a left sidebar just like this and it's going to have a right sidebar as well it's a pretty complex interface but it works especially for social media applications when you have a lot of data to showcase so in between the left sidebar and the right sidebar of course we have our section and that section is going to have a class name equal to main Dash container so this is going to contain the majority of our application and within it we can have a div that's going to have a class name equal to W Dash fool meaning full width and Max Dash w-4xl so on large devices it's going to go only as far as Max W4 XL and we can render the children within it there we go now you can notice that we have a lot of red squiggly lines here left sidebar top sidebar none of this has been defined so let's create a new components folder outside of the app folder we created outside because in the app we only put the files and folders which we want nextgs to render on the home page don't forget nextgs supports file based routing so that's why I want to create it outside and we can start by creating a couple of folders this time we're going to create a folder called shared and this is going to include some shared components we can also create another folder called forms and even a third one that is going to be called cards so essentially we have three folders within the components folder just like so cards forms and shared and later on we're going to have an additional one for Shad CN components now inside of shared we can create a new component and that's going to be top bar dot TSX at the same time we can create all the other ones such as the Bottom bar dot TSX we can also create a left sidebar dot TSX and a right sidebar dot TSX I believe this is all that we need for now let's get started from top to bottom with the Bottom bar that's going to be function Bottom bar and it's going to Simply return for now we can make it return an H1 that's going to say Bottom bar and finally export default Bottom bar this is all that we need and we can copy this and also paste it to all the other components but of course rename it from Bottom bar to left sidebar we can repeat the process for the right sidebar and finally for the top bar B with a lowercase b and there we go so now we have these different components and we can go back into our app into the root layout.tsx and you can double click the top bar and press Ctrl space or I believe it is command space on Mac and it's going to automatically recommend you the import and we can repeat the process for all the other components and if you notice at the top you can see we have Imports for these four different components coming from add forward slash components shared top bar left sidebar right sidebar and Bottom bar this means that we no longer have any errors and it also means that the general structure of our application is done we can see the home in the middle left as well as a top bar and left sidebar on top because they're not falling into the layout nicely as of yet and then we have the right sidebar and Bottom bar at the bottom now to start creating that layout we can start with the top bar and then move to the left sidebar and you're gonna notice soon enough all things are going to start falling into place so to get started with that we can put our browser side by side with our editor this way we'll be able to see the changes that we make live and we can close all of the currently open files and just open up the top bar as it's on the top so it makes sense to start from it first the Stop Bar is actually going to be a nav so we can have a return statement that's going to have parentheses like so and we can transform this H1 tag into a nav tag that's going to wrap everything else this nav tag is going to have a class name equal to top bar and if you're wondering what this Stop Bar is you can simply search right here in the search for top bar and it's going to show you the top bar globals.css file and this one line is applying the position fixed top 0 Z index of 30 Flex W full as well as BG dark 2. so it's just generally setting up the layout for our navigation bar so we don't have to type it by hand but if you're wondering how to get to the Styles now you know also if you're any point wondering what a specific Tailwind class does simply go to tailwindcss.com go to Quick Search and type it out if you type Flex you're gonna quickly see how you can write those classes another cool thing is this Tailwind CSS cheat sheet where you can also search for flex and you can see all of the different properties that you can use that Flex inline flex and so on Tailwind is pretty intuitive to use so once you get to understand it it's going to allow you to speed up your CSS workflow significantly great now within this nav we can create a new link component as we usually do and this link is going to come from next forward slash link so we can also double click it and import it this link has to have an href and this specific one is going to point to forward slash it's also going to have a class name equal to flex items Dash Center and gap-4 inside of it we want to render our logo so let's render a next gs13 self-closing image tag as well as imported coming from next image then we can give it a source equal to four slash logo.svg and we can give it an ALT tag equal to logo as well as a width of 28 and height of 28 pixels as well also below this image we can have a P tag that's going to have a class name equal to text Dash heading 3 Dash bold text dash light dash 1 which is for the color and on Max Dash XS devices we want to set it to Hidden so just like so because it won't fit in the screen so there we can simply say threads now if we save it you'll be able to see that we have our threads but unfortunately the logo is not showing that's because silami I haven't yet provided you with all the assets so in the description down below you'll be able to find a zipped assets folder simply download it unzip it and then paste it within your public folder right here public assets and then within it you're going to have your logo.svg let's save the file and correct the path to forward slash assets forward slash logo.svg and immediately we have our cool modern threads logo now while we're here let's also make the app better by implementing the logo right here as the favicon and fixing the title as well so we can go to our layout and here we can update the metadata as a matter of fact we can copy the metadata from the auth layout right here and simply paste it within this layout here in the root that's going to change the text or the title to threads and now we have to also add the favicon which we can do by modifying this favicon right here from next.js131 to our new logo so to do that we can go to our assets find our current logo SVG and we can reveal it in File Explorer or finder if you're on Mac that's going to give you a view that looks like this next you can go to favicon.io and you can simply find a converter then you can drag and drop your SVG and click download it's going to provide you with a zipped folder and the only thing you need from there is the favicon file what you first need to unzip by dragging it somewhere else for example to your desktop and then from your desktop you simply need to drag and drop it right here into your app folder once you do that and close the layout and the layout and go back to your app and reload the page you'll be able to see your favicon your title and now we have also our top bar done these little details really go a long way with that said let's continue with creating our top bar we can go below our initial link and create a Dev this Dev is going to have a class name equal to flex items Dash Center as well as a gap-1 to provide some spacing between the elements then within it we can have a div and that div is going to have a class name equal to block usually but on median devices it's going to be hidden so we want to make it mobile responsive right off the bat in case it doesn't fit on the screen we can easily remove it inside of here we want to play with some clerks components so we can say signed in and this signed in is coming from ad clerk forward slash nextgs and then no longer is it in beta so it's going to come from ad clerk forward slash nextgs and this is incredibly powerful I remember for the past years we were writing code that looks like this open a new Dynamic block then you have to create functionality to figure out is user logged in so const is user logged in and then this will dynamically be set by some kind of a function or API call and then ultimately it would return a true or false value then you have to check it you have to open up a new ternary operation provide the other case as well and then in here you would write the actual code if we're locked in or if we're not logged in with clerk you can immediately call this component and the code within it is only going to appear if you're signed in how cool is that so if we are signed in the only thing we want to show is simple it is a sign out button it's going to look like this and it's also going to come from the same package clerk next.js so we can import it right here at the top you can modify that sign out button however you please in this case it's going to be a div with a class name of flex and cursor Dash pointer and within it we can have an image and this image is going to have a source equal to forward slash assets forward slash Lookout dot SVG it's going to have an altag equal to logout a width of 24 pixels as well as a height of 24 pixels as well and it's going to be a self-closing Nexus 13 image tag so we can do it like this and for now we cannot see anything but no worries because we're gonna look into that later on for now we can go below sign out button below signed in below the div and we can use another one of clerk's great components which is called an organization Switcher and it's going to be a self-closing component which is also going to come from at clerk forward slash nextgs so we can import it here and you can immediately see our profile icon and if you click it you have the potential to create your own organization and it shows you your account right here one of the props of the organization switcher is the appearance probe which allows you to provide some additional elements and I figured out that we can add an organization Switcher trigger and we can add a padding y of two as well as a padding X of four which is just going to provide it a bit of space so it's going to look better in general great this is all that we need for now we're gonna fix the layout later on but if we go even to a smaller size you can notice that this logout button appears the reason why we didn't see the logout button at the start is because we have this block MD hidden and that's because usually we're going to have our logout button on the left sidebar and only if we're on small devices we're gonna make it appear on the top bar just like a little icon let me show you what I mean this right here is the app's design which we'll be able to reference throughout this video and as you can see on the desktop size we have our logo button on the bottom of her left sidebar but on mobile devices it's going to appear right here on the top great with that said our top bar was pretty simple so now we can move to our left sidebar by closing this file going to our layout.tsx and then opening the left sidebar our left sidebar is going to be a section so we can simply say a new section component right here it's like a div but it has a semantic meaning we're going to give it a class name equal to custom Dash scroll bar as well as left sidebar and within it we can have a div and that div is going to have a class name equal to flex W Dash full for full width Flex Dash One Flex Dash coal so the elements appear one on top of another gap-6 for spacing as well as padding X for horizontal padding and within it we can map over our sidebar links in this case we could have a link component and then have a lot of stuff into it and then duplicate it many many times but what you're going to usually find is the best practice with these types of lists is to create them somewhere else for example in the constants folder so let's go to our file explorer and right in the root of our directory we can create a new folder called constants inside of the constants we can have only one file called index.js and in the description Down Below in that same GitHub gist where you got the styles from you can also find the index.js of constants and simply paste it right here this is about four lines of Simply some data structures and I mean simple data structures like an array that contains a list of objects that contain a link to an image a route and a label now we'll be able to use these links to map over them and we don't have to Define each one separately so now within the left sidebar we can open up a new Dynamic block of code and say sidebar links dot map and we can map over each individual link and return something for it for example for now it can be a div that's going to say link of course we have to import the sidebar links which are coming from constants we're exporting them from here so to import them we can go to the top and say import sidebar links coming from at forward slash constants there we go and now if you save it you won't be able to see because the color of the link is now dark but if you hover over you can see we have one two three four five or six links and now we can style them separately so let's turn this div into an actual link and we have to import it from next link it's going to have an href which is equal to link dot route and we can put this in a new line so we can fit all of the properties nicely the second prop we can pass to it is going to be a key because we're mapping over the elements which is going to be a link dot label and finally a class name equal to left sidebar underscore link and within it we can render an image which is also coming from next forward slash image it's going to have a source property equal to link Dot imgurl all uppercased URL I'll tag equal to link.label a width of 24 as well as a height of 24 as well and most importantly below the image a P tag that's going to render the link.label but of course we have to give it a class name for it to be visible where we can say text dash light dash 1 and on Max LG we want it to be hidden now if we save it you can see the icons so the speed tag is hidden on the devices of this size but if we make it larger you can notice how we can also see the full labels of this specific text but on smaller devices even just seeing the icons makes a lot of sense this is the home search activity create a post community and a profile so all of this makes sense for now we can keep our browser expanded so we can see all of them in their full Glory but now how do we know which link is currently active well to be able to know that we're going to turn this map from a regular instant return to an actual function with a return statement which means that we have to wrap this in a pair of brackets have a return inside of it and then also at the end we have to wrap it with an additional brace and that's going to look like this we're closing the function opening then we're closing another one and then finally it's going to look like this it is a bit of a more complex structure but we can now add some additional logic right here for example figure out if the link is currently active and to be able to know that we'll have to import something from next navigation so let's do that right away we need to import use path name as well as use router coming from next forward slash navigation that's going to allow us to know the current URL we're on so let's define those hooks at the top by saying const router is equal to use router as well as const path name is equal to use path name what we can do now is uncommon the is active and say is equal to open a pair of parentheses and then say if path name dot includes the current link dot route and link.rout.length is more than one meaning it's not just home or if path name is directly equal to the link dot route there we go and of course I have to spell path name correctly so if I do that we have our is active variable so now we can change the style of our sidebar items based on that variable and also it says that the use router which is a hook only works in client-side components so at the top we have to add a declaration of use client to denote that this is indeed a client-side rendered component because we're using a hook so now here within the class name of a link we can make it a template string just like so open up the new Dynamic block and say if is active in that case we can render a string of BG Dash primary Dash 500 and of course we have to close it properly right here and I believe we're good so now we can see the home page is currently on if we go to search we get a 404 because we haven't yet created the search route or the search page so as soon as we create the pages for all of these different components we'll be able to switch between them and the next active route is going to immediately move seamlessly now at the bottom we can add that log out button as we discussed we already have the template for it right within our top bar so let's go there and let's simply copy this entire signed in block and paste it right here below this div but before create another div that's going to have a class name equal to margin top of 10 to divide it a bit and padding X of 6 for horizontal padding and now we can paste it right here of course we must not forget to import all these components so we can go to the top bar and simply copy the import statement from add clerk forward slash nextgs and paste it right here at the top as well we won't be needing organizations which are here though so now we save this and you can see this icon but of course we want to just make it a bit different here so what we can do is also render a P tag below this image that's going to say log out and we can give it a class name equal to text dash light Dash 2. and on Max LG it's going to be hidden as for all of our nav items if we save that you can see a log out but of course we have to provide some additional styling such as gap-4 and padding Dash 4. if we save this it's going to look much better and much more uniform with other items also we have to add a sign out callback right here on the button by saying sign out callback is equal to a callback function where we simply use the router.push and go to four slash sign in so once we do sign out it's going to redirect us back to the sign in great now as you can see there's some weird kind of scroll happening over here but later on we're going to fix that as you can see immediately by having the top bar and by having the left sidebar the app is looking much closer to the design so what we have to do still is the Bottom bar as well as the right sidebar so let's move into our root layout and from there let's start creating our let's do a bottom bar first just to spice things up a bit our Bottom bar is going to be pretty simple we're going to wrap everything within a section component similar to a div as we discussed already and that section is going to have a class name equal to Bottom bar it's also going to have a div and that div is going to have a class name equal to Bottom bar underscore container and inside of there we can also Loop over the sidebar links dot map where we get each individual link like so and now you might be wondering what is this Bottom bar for if we comment this out quickly what are we going to put here we already have a lot of stuff on the top on the left sidebar why should we map over the sidebar links one more time so on the design there is no Bottom bar here why do we need it also in the finished version of the application nothing seems to be here think about it the Bottom bar is going to be used for our mobile navigation yep we're going to have a native mobile like feel on our web application Isn't that cool this means that you'll be able to Traverse the application using your bottom bar right with your thumb it is the easiest way to Traverse the app and it doesn't take a lot of screen real estate imagine if we had our current APP and if we go to the mobile view where are we going to put it we have to have the content in the middle but if we now have a left sidebar it's going to take at least 10 to 20 percent of the screen which is way too much that's why we're creating this mobile sidebar for mobile devices that means that while developing it we can stay at the mobile view and you can notice we're going to display same links as we're displayed in our left sidebar so for that reason we can go to our left sidebar and we can copy this entire sidebarlinks.map and paste it right here into the Bottom bar of course we'll have to import a lot of things such as the sidebar links from add constants and we'll also have to get the path name right here we'll also have to import the link from next link image from next image and use path name from next navigation I believe this is more or less said now if we save it you can see that we have to turn it into a client component as well so let's copy the use client directive and paste it right here at the top and believe it or not right out of the box this is going to work perfectly because we have made our link responsive but of course to improve it further I created a special class called Bottom bar underscore link which is going to make it look even better there we go and then another really cool thing we can do is we can make this work for tablet devices as well so if we expand it just a bit you can notice this is starting to get a bit wide right but we can also display a limited amount of text right here so let's do just that instead of this P with a class name of Max LG hidden we can turn this into text subtle medium like so text light one and Max Small hidden so on Max small devices it's going to be hidden and now we can see the text but in this case we don't have space for two words for create thread so what we can do is we can take the link label and just modify it slightly by saying link that label that split we can use a ternary operator right here such as forward slash which is for Words plus and then do this and get just the first character so that's going to look like so of course we have to end the regular expression right here so this is going to get just the first word and now all of the words fit nicely this is for bigger phones or tablets and we have this for regular phones and then once we go back we have our great sidebar which looks like this isn't this great thinking about responsiveness right from the get-go this means that we're done with the top bar the left sidebar and the Bottom bar and now we are ready to move to the right sidebar we won't do a lot of work there because we're going to implement its features later on but it's good just to have it for the sake of the layout so our right sidebar is also going to be another section that's going to look like this and it's going to have a class name equal to custom scroll bar same as the left sidebar and it's going to say right sidebar within it we can create a div and that div is going to have a class name of flex Flex Dash One Flex Dash coal for the elements to appear one on top of another and justify Dash start it's also going to have an H3 which is going to say suggested communities if we save this and for now we cannot really see the right sidebar right here it could be because we don't really have a lot of space here the right sidebar is only going to be visible when you have a lot of space usually it's going to get hidden we have to have about this much space for it to be visible but even though we have this much space it's not right here it could be that it's hiding here yep you can see it right there suggested communities even though it should be showing on the right side the reason for that is in the layout here we haven't provided a class to this main it should have a class of flex which is going to make the elements naturally flow within it so it's going to be Flex as well as Flex Dash row now if we save this everything is going to look so much better because we won't have that huge scroll to the bottom and now the right sidebar is here and we can see suggested communities so let's go into the right sidebar and let's modify this text just a bit that's going to be H3 with a class name equal to text Dash heading 4 Dash medium and the color of text dash light dash one that's going to make it pop immediately right now we don't really have a lot of communities to show so we can leave this part empty but we can duplicate this div one more time below and there we can say suggested users so we'll be displaying both the users and the communities on the right side and this is more or less it as I said we don't want to focus on the right sidebar too much because we don't yet have the logic to display it great believe it or not the layout is now here we want to expand it and as you can see even though we have the top bar and two sidebars there's still plenty of space for us to work on the content of the website so this is great and let's see if the logout functionality works as well so we can try to sign back in I clicked log out and I was redirected back to the sign in and now I'm gonna try to sign back in and this time we're redirected to the onboarding which we must not forget here we'll be able to choose the profile photo in case we don't have one from Google yet and we'll also be able to select our username and choose our bio right here so we're gonna do this really soon for now we can go back to the home and see everything we had before wonderful work now let's finalize the onboarding so that we have the full authentication logic done and then we can focus on implementing the back end for creating and showing our posts and before we proceed with the onboarding there's just one small little detail that's been bothering me you can see that this profile photo is a bit too far away from the end that's because here I'm not sure if you can see it but in dark letters it says personal workspace and it should be saying that with the light text so how do we modify Clerk's component to work for our dark mode well easy within our Visual Studio code we can open up our terminal and install a package called add clerk forward slash themes once installed we can go to our top bar into the organization Switcher and within appearance we can add a base theme which is going to be not a string of dark but rather dark which has to be imported at the top by saying import dark coming from add clerk forward slash themes and now it's going to use that dark theme so if we save it you can see that now it switches to the white text color as well as the div also change to the Dark theme to support what we already have this is wonderful don't touch the Creator organization yet we're gonna play with that a bit later once we have the basic structure of our app done great with that said now we can move to implementing our own boarding process which is pretty cool you never see tutorials about it but a lot of great applications have a couple of steps you have to go through as soon as you create an account so I'm gonna teach you how to do that right away to get started with the onboarding we can close all of the currently open files and go to auth onboardingpage.tsx right below this H1 we can create a P tag and say something like complete your profile now to use threads and we can also give it a class name equal to margin top of three to divide it a bit from the title text Dash base Dash regular as well as text Dash lite-2 we should be able to see it right here now of course this has to be centered so to this main we can give it a class name equal to MX of Auto for margin horizontal Flex Max w-3xl and already this should move it a bit closer to the center we can then give it a flex call to make the elements appear one on top of another justify start padding X of 10 as well as padding y of 20. and that's going to make it appear right here now below that we can render a section and this section is going to have a class name of margin top of nine as well as BG Dash dark Dash 2 and padding of 10. this is going to create a bit of a lighter rectangle and we won't have to create all of these functionalities ourselves everything that has to do with users and authentication we get by clerk which is phenomenal within this section we're going to create a new component called account profile and this is going to be reused across various places of our app so let's go to our components forms and then within the forms we can create account profile dot TSX that component is going to be use client because we'll be using forms and it's going to be const account profile is equal to an arrow function and it's going to Simply return something like a div that for now can say account profile and of course we have to do export default account profile great now if we save this we can import that account profile from ad for slash comments forms account profile and now we don't have an error but we do have an account profile now to that account profile we can pass some things such as the data about the current user and for that thankfully clerk provides us with current user which we can simply get from add clerk forward slash next JS and we can say const user is equal to a weight current user and since we have a weight this has to be async and now before we pass that user over to the account profile we want to create a new object called user data soon enough you're gonna see why this user data has to have an ID which later on will come from database so we will have our own instance of the user in the database as well so we can attach different threads to them it's also going to have its own object ID and this will also have to come from the database so later on we'll have a new fetch user but fetching it from the database not the currently logged in one and that can be something like const user info and later on we'll be able to get it for now it can be an empty object so we're going to combine in the user data the data from the user info as well as the currently logged in user so I was wrong when I said that the ID will come from the database the ID is the actual ID of our currently logged in user but the object ID is the user info dot underscore ID and we can also add a question mark to be sure it exists so this is coming from the database of course now typescript is going to complain that it doesn't exist but we're going to fix that later on we're going to also have a username right here and the username is going to be equal to user info question mark dot username or it can simply be user that username so whichever way we get it it is fine then we have the name which is going to be equal to user info dot name of course we can add a question mark or it's going to be user DOT first name in case we already have one from Clerk or finally it can be an empty string if we don't have anything else we have the bio which is going to be user info question mark.bio or an empty string and finally an image and this image is going to come from user info question mark dot image or it's going to be user dot image URL so this way we always ensure that we have at least something and of course I meant to say or right here even though it's complaining these props do not exist on this user info object they will once we actually fetch this data from the database so now that we have this user data we can pass it over as user is equal to user data as well as BTN title which is going to be continue this account profile is going to look different depending on where we call it and you can immediately see typescript is complaining because it doesn't yet know that this account profile is accepting any prompts so let's go to it and let's make it accept sum we can do that right here by saying we're going to accept a user as well as a BTN title is of a type of props and we can Define the props as an interface where we have the user and this user is going to have an ID of a type string it's going to have an object ID of a type string as well a username of a type string a name of a type string a bio and an image all of type string as well as a BTN title of a type string and now we can start creating the account profile structure and this is the first time that we're going to start using Shad CN components Chad CN is a library of beautifully designed components that you can copy and paste into your apps it is accessible and customizable using tailwind and best of all it's open source as you can see this really nicely aligns with the look and feel of our general threads application and the forms look great as well as you can see right here so let's immediately get some of their components let me show you how we can go to components and then we're gonna go to form you can see that they're using react hook form and Zod and forms are usually tricky but they're also one of the most common things you're going to build in a web application so they give you everything out of the box the anatomy of a form including form fields and all that other stuff as well as an example so here is how we install different components using shots DN you don't have to install the entire heavy library of components you're not going to use you can simply add that form which is going to then be added to your file tree so let's try it out I'm going to copy this command from ui.chatcn.com forward slash docs components form and I'm going to use npm as well we can go in our terminal and paste that Command right here MPX shot cnui latest add form it's going to ask us to install shot CN which we can say why as in yes and now it's saying that we cannot yet create a new component we first have to initialize Shad CN so let's go straight through the docs and let's go to installation with nextgs of course it's going to ask us to run MPX shot CN UI latest in net so let's do just that and then it's going to ask you many questions on how you want to set up your components so let's Zoom this in let's expand it a bit further so we can see it all well and let's start answering those questions would you like to use typescript yes we're going to use typescript which style would you like to use we're going to use the default Style which color would you like we're going to use slate in this case the global file is in the app global CSS do you want to use CSS variables for colors we're going to choose no here where is your Tailwind config it is the default tailwind.config.js location and you can also configure the import which we're going to leave as it is by pressing enter utils also leave the same where it is and then are you using react server components we're going to say yes write configuration to components Json yes so we can type Y and then proceed this is going to write components Json as well as initialize the project and install the necessary chat CN dependencies there we go so one thing that you can notice is this new file that you can see right here components dot Json this contains everything we need to get started so now if we reopen the terminal and run the command we ran previously MPX Chad C and latest add form it actually should add the form component to our file tree ready to install components and dependencies proceed yes and it's done now if you go to components and then UI you will see that three of these components have been installed button form and label and we can start using them from within our app so now let's collapse our terminal now let's make this entire thing return our form but the question is where is that form coming from and it's coming from Shad CN but as this cast chat CN creates all of the instances of the components for us so we can say import form and that's going to be coming from let's put it in multiple lines from add forward slash components forward slash UI forward slash form and now we have our form right here but we also need to do something else we need to declare the form data that we're going to pass into it so we can say const form is equal to use form in this use form is coming from a package called import use form and the package is actually called react hook form it is a package that significantly simplifies the work with our forms so now we have the use form we also have the form and we have to Define it so what do we have to pass to this use form well it's going to be an object and there we can say resolver what is this resolver we're there another package that simplifies the work with forms is going to come in handy and that package is going to be called Zod is a typescript first schema validation with static types essentially it's going to allow you to really easily create different schemas for the fields in your form so let's utilize it in just that way by going back here and going all the way to the top and importing Zod resolver from add hook form resolvers Zod so this is connected to react hook form and then we can say resolver is Zod resolver and to it we need to provide our own validation and that validation we're going to specifically create by going to our lib folder and creating a new folder called validations inside of validations we can create a new user.ts file because this form is for saving the user data so how is that going to look like well it is as simple as importing everything as Z from Zod then saying export const user validation is equal to Z dot object where we pass an object inside of it and Define different fields such as profile underscore photo is equal to Z dot string which we call as a function dot URL dot non-mt this essentially ensures that the profile photo is a string of a type URL and it must not be empty we can also Define a name name can be a z dot string and then we can pass additional requirements maybe a Min it must be longer than three characters and then a Max as well no longer than 30 characters in case you want to provide an error message you can do that right here as the second part where you say message is equal to minimum three characters but I do believe it's not a necessity to provide it you can if you want to make your messages more descriptive now we also have a username so we can duplicate our name and say username and it's going to be the same thing for it as well as a bio that can be a bit longer maybe up to 1000 characters so bio right here now we're exporting this user validation and we can use it right here within Zod resolver by saying user validation and it's going to come from add forward slash lib forward slash validation forward slash user and now to perform the second thing we can pass our default values default values is going to be an object that's going to include as you might think the default values which we can pass for now we're going to leave them empty so we're going to say profile photo is an empty string then we have a name which is also an empty string a username which is an empty string and a bio which is an empty string later on once we Implement edit profile or something else we can immediately pull some data right from here as a matter of fact this might be useful right off the bat because we'll be getting some data from clerk so we can immediately display it within the form but we're gonna do that soon enough so now we have this form and the way it works with react hook form and the shot CN is that you have to create that form schema you have to do it here you have to do a function on submit which we're going to do really soon but as you can see you have to do what we did form use form Zod resolver default username and then you build out the actual form so let's use their example to build it as you can see we have build our form and we need to import all of these things coming from chat CN so let's do just that by copying this part and going all the way to the top and importing all of these components coming from UI form they also use an input here which we haven't yet installed but I think it's pretty intuitive we just call MPX shot CN latest add input and that's going to install an additional component which we can use within our code really easily and that truly does work it was immediately installed and you can see an input appear within our UI folder what else do we have a simple form with a simple button so let's try to copy this entire form and let's paste it right here instead of our mt1 we don't yet have an on submit function so let's also copy that from here it's just an empty Handler so we can paste it right here on submit we have type of form schema which I believe we don't need at this point in time so we can just leave it as values and yeah it does look like we need a type so how do we get the type I believe it's just coming from Zod it's going to automatically allow us to use the type as well and the form schema is actually a z dot object so for us the form schema is the user validation that's going to be the type of the on submit because we're going to submit whatever has to be within this specific object great so now we have our form and we're finally ready to check it out and see how does it look like in our browser so going back here we have onboarding complete your profile and then we have a username this is your public display name looking pretty good right out of the box but it's looking a bit too white we want to have a dark background and we wanted to suit our needs let me show you how it's looking on the finished site if we go to threads and if we log out I'm going to log in with a new account then we can see another window from clerk asking us to add the username I'm going to do JS Mastery one and click continue and then we are redirected to the onboarding where you can see how this model should look like it should be dark it should have a profile photo and it should have three fields for your name for the username and for the bio as well as the button this right now is looking far from it but we are getting there so let's continue working on our form let's get started by using the right form Fields the ones that we need in our app and those are going to be the name username and the bio as well as this great profile picture selector so let's get started we first have our form and we can give that form some class names so let's put this into two lines and right here instead of just space Y8 we can say Flex Flex Dash call justify that start and GAP Dash 10. for now this just provided some padding we have our first form field of the day form control and the name is going to be profile underscore photo we will render a field that has a form item and that form item can have a class name equal to flex items Dash Center and gab Dash 4. then we have a form label and this form label is going to have a class name equal to account Dash form underscore image Dash label it's a bit of a long class but it nicely signifies what it means inside of here we want to tap into the field.value and if it does exist then we want to render a self-closing image tag of course we have to import that image from next forward slash image and we can set a source equal to field.value the alt tag is going to be profile photo the width is going to be 96 pixels as well as the height it can have a priority loading this is a great prop by nextgs and it can have a class name equal to rounded Dash full as well as object Dash contain so this only works if we have a profile folder to show if we don't have a field.value then we can render something else and that something is also going to be an image so we can clone it but instead of a source of field add value we're going to show forward slash assets forward slash profile dot SVG the width and height can be much smaller about 24 pixels right here and it doesn't have to have priority and it can only say object contain so let's save it and let's check it out as you can see we have this little icon and then you'll be able to choose your file to add it there after the form label we have the form control that form control can have a class name equal to flex dash one text Dash base Dash semi bold as well as text Dash gray-200 inside of it we have an input this input currently has a placeholder of chat CN and we're also spreading all of the props but in our case it's going to be an input of a type is equal to file it's going to accept an image of all types it's going to have a placeholder that's going to say upload a photo and a class name equal to account Dash form underscore image Dash input and of course an on change property or Handler where we have a callback function with an event where we call the handle image function pass the event as well as the field dot on change of course this handle image is a function that we are yet to create so let's scroll up and above on submit let's create const handle image that's going to be a function and for now you can simply get the event and say e dot prevent default to prevent the browser to reload in this event of course has to be of a specific type so it's going to be change event coming from react so make sure to import it at the top alongside the event we're also getting the field change which is equal to a callback function like so where we get the value as a string as the first and only parameter and it doesn't return anything so it is void this is how react hook form works you immediately get the value right here great so now we have our input of a type file which renders an image so now this is going to look like this and you can actually input your file in this case we won't be needing a description so we can remove the form description as well as the form message these came from when we copied chat CN and this is now looking a bit cleaner we can now proceed to create other elements as well by copying this entire form field which is pretty big and then pasting it below to create the one for our name this one is going to be much simpler we have a form field that has a control of for control it has a name equal to name and in this case within the form label we don't have to put these images we simply have to say name okay because this is for the name field we don't need this class name rather it's just going to be text Dash base Dash semi bold and text dash light Dash 2 The Gap can be three right here and we can also add W Dash full so it takes the full width of the container and as the form control it's going to be much simpler as well the class name is going to be account form underscore input not an image input and no Focus we don't have to provide an on change because we're going to spread the default Field properties so it already knows that this is a regular type text input that's why react hook form simplifies things so much so if we save this we immediately have a name input and now we can duplicate this one two times one two the second one is not going to be a name but rather it's going to be a username so we can also say username right here and the last one is going to be the bio so it looks like I've exchanged those two the username is right here the second to the last one so we have a username and then the last one is a bio the name is bio as well and we don't want to render a form control input but rather a text area because we can enter more text in there that's going to look like this and it's going to have rows is equal to 10. and of course we have to import the text area by first installing it so we can say MPX chassis and latest and we can do text area you can see how intuitive it is I don't even have to search for it in the documentation because I know it exists so once I install it it is done and now I can go right here where we have the input I can duplicate that and I can say text area coming from text area there we go now if I scroll down we have our input we have our text area this is looking much closer to what we need to have which is this but we're getting there we still have some work to do and finally we have the button this is it when it comes to the layout and the structure and all of the elements of our form but of course now we have to turn this into this so let's get started with styling when it comes to styling we can check if our Styles work by modifying our button so for example we want to give it a class name equal to BG Dash primary Dash 500 and this should apply our Tailwind config but as you can see it cannot recognize the caller which means that it's not picking up on the styles so our account profile is a component used within the onboarding and the onboarding is a component used within the page that is within the auth layout so if you go into here you can notice that we're importing the globals.css but for some reason we only have the Tailwind Imports there nothing else at the start of the video I did tell you to copy and paste the modified globals.css but it is possible that it got overwritten while I was installing chat CN so if that happened for you simply add all of our globals one more time and it is possible to have to do the same thing for our Tailwind config yep it looks like that it overroded so we need a Tailwind config from the description as well all of these files are in the GitHub just down below the tailwind.config.js as well as the global CSS so simply copy it import it in the layout and immediately you can see that on our localhost 3000 onboarding it looks much closer to what it should look like it's just that chats Yen overrode our files so now we know it for the future let's take a look at the errors and it says there's a hydration error it fails because the initial UI does not match what was rendered on the server this is one of the more common next GS errors and we can read a bit more about it right here this happens with the incorrect nesting using type of window or even local storage sometimes even by using incorrectly configured CSS libraries but another really common reason which you might never suspect and which can be bothering you for hours is browser extensions so we often have many extensions in our Chrome or different browsers but sometimes these are messing with our application and next year is being new as it is allows for many great benefits but sometimes extensions mess with it so as soon as I disabled one of the extensions everything started working normally and no longer are we getting this error so if you have the same one the error is not in the code rather it's one of the extensions messing with your code great now let's compare this one final time to the final version and as you can see the labels are appearing to the left of the inputs pushing them to the right side and in the final version they appear on top so let's quickly get that fixed and that's going to be in the account profile within our form Fields specifically where we have the form a label the form label for the first four input is okay but for the input once Let's see we have the form item which has the class name of flex we actually don't need item Center in this case we needed that just for the image not for the regular ones we need the full width as we have here and then Gap three and then the form label has its corresponding Styles we have the form control below and our form control in this case doesn't have to have any Styles so it's even simpler than we anticipated and finally we need a flex Dash call here because that's going to make the name appear on top of the input and there we go this is the layout that we wanted to have wonderful later on we're going to also fill these inputs with additional predefined values that we're going to get from clerk so now we have to copy the form input label and control and the start of the input for other elements as well so that's going to be our second or third form control for the username so let's modify this right here and rename this to username and finally the last one is going to be the bio so we can modify all of this and then just rename this back to text area now if we save this you can see it is looking exactly like it does on the finished website and of course it's not going to say name it's going to say bio right here there we go now let's work on getting the default values from our sign in because remember the onboarding happens after we sign in so we already have access to some of the information right here so let's do that next don't forget we're already passing the full user object through props so we can simply say user question mark dot image or an empty string we can say a similar thing for all of these so it's going to be user.name username and bio and we can simply modify this so name username and bio and if we save this you can see that it's going to break but not because we did something wrong but because next.js is protecting us in this case we're trying to render an image that's coming from clerk clerk immediately configured it once we logged in using Google it picked out the user profile so now we have to allow the hostname imageclerk.com to allow to post images on our nextgs app and you do that within the next config so let's go to the next adconfig.js and then inside of there we have to add a new property called images that's going to be an object that's going to have remote patterns equal to an array where we have an object with a protocol equal to https and then a hostname equal to img.clerc.com this is going to allow images from Clark and we can also allow all the other patterns that we're going to use later on you can get the modified next config.js from the github.js down below so simply paste it and here we're going to add some additional patterns but even more importantly we are allowing the experimental server actions to be true because this is going to be necessary later on for our Mongoose project if you don't add server components external packages is equal to Mongoose it's not going to know how to render those Mongoose actions so it's not going to work so please while we're here in the next config get it from down below and put it here with that said if we save this go back and reload let's see if it's going to automatically read the changes in the next config or not it looks like not so we'll have to rerun the application by running npm run Dev and there we go it automatically picked out the photo from my Google profile the name as well as the username this is pretty cool and then we can also add our own bio and then later on we'll be able to submit it so for now we have this pretty cool form which is working right off the bat because we're using the react hook form Zod and all that good stuff we're going to explore how the validation Works later on so you won't be able to submit values that don't make sense and before we proceed we also have to make our photo upload work because not all users will be coming from Google we also allow our users to come through GitHub or just through regular email and password so they still have to get their cool photos uploaded right here and add all of these additional Fields so let's get started with implementing the function which we have already started working on which is the handle image upload to make our image upload work we can go all the way to the top of account profile and create a new use State field of course we must not forget to import use state from react and we can call it files and set files of course the set files being the setter function to the files and at the start it can be equal to just an empty array the type of this used State can be set to file or more specifically an array of files which is going to look like this we can go into the handle image and there we can read the file from the file reader so const file reader is equal to new instance of the file reader which we call as a function and then we can make a check if e.target.files.length meaning if there is something in that case we can read the file by saying const file is equal to e dot Target dot files zero then we can check right here it says that the files does not exist on event Target element that's because this is not a regular element it's an HTML input element so if we do it like this and if we fix this it should be good but now it's complaining that it says returns a file list object and a file type input but now we get a different error saying e.targeted files is possibly null so maybe we can make a check if e.target.files and then etarget files that length is greater than one in that case we can make this here we can also then set files which can be equal to an array from e.target.files if not file.type DOT includes image we simply exit out of the function with a return and then if that is not the case we can say file reader Dot on a load is equal to an async function where we get the event and we can read the URL by saying const image data URL is equal to event dot question mark dot result question mark dot to string or just an empty string in case it isn't there and finally we can call a field change which allows us to update that by passing the image data URL we're doing this because we're using react hook form and that's how you update the field and finally below we can say file reader dot read as data URL where we pass in the file this should allow us to change the image so I'll try to change it to this favicon and we can see favicon right here but it didn't change in the display so let's look into our form or we have the form field we have the image if there is a field.v value of a profile photo then we display field dot value right here which should update the actual photo so it's called profile photo and we are updating it right here user dot image or nothing so if I reload the page and try one more time yep it still doesn't update it let's go to our function that sets our image which is right here handle image e field change so let's see if we're properly passing the value and this is looking good to me so let's move away to it there we have the E dot prevent default which we need to have to prevent a browser reload file reader initializing a file reader right here after checking whether the files actually exist and this here should be larger than zero not larger than one because we're working with arrays so now that I fixed it it should actually pick up the change here and there we go it's working so now we can properly change our profile photo as well we can change the name username as well as the bio and the last thing we have to do is implement the on submit function the on submit is going to re-upload the new image and it's going to update the user in our database we're talking about a user in our mongodb database which we haven't yet created so once we finalize the front-end part of the unsubmit and once we pass the data to our backend we'll be ready to start creating the backend side of our application isn't that exciting I know that they've been working a lot on this already but don't forget we have the complete authentication we're gonna have the onboarding soon and we have the complete layout ready for us to work once we get to the home page but once we finalize the account creation we'll be able to move towards the back end implementing all of the Fantastic backend logic of our project so let's finalize the submit function and soon after let's get started with the back end to get started with our on submit function we can first get the value from our profile photo and we can do that by saying const blob usually the value from an image is called a blob and we can get that from values dot profile underscore photo because we are setting it using react hook form right above here we have the set files but also field change and since this field is cold profile photo react hook form is going to update it automatically which is why we're going to have this value but of course we don't know if the image has changed so we can say const has image changed and that's going to be equal to is base 64 image which is a function to which we're going to pass that blob now where is this function coming from well it's going to come from our utils utils are known as utility functions functions which you can reuse across your code so let's visit our utils.ts within the lib folder it was automatically created for us by shat's Yen but now we can add some additional things to it in the description down below you can find a GitHub gist and within that GitHub gist you'll find the final utils.ts file which you can simply paste right here inside of there you're going to notice some functions which might seem a bit too over complicated but not to worry they weren't created by me they were created by chaji BT so as you can see right here we have this is base64 image function which we want to use which takes in the image data as a string or a blob and it simply tests the regular expression to see if this indeed is the image we're talking about so this is pretty cool I got this by simply saying write a JavaScript function block of code that checks whether an image is base64 something like that and it's going to provide you with the perfect function that you can use but of course you can check it if it works now we have access to this export function is base64 so we can go back and we can import it from add forward slash lib forward slash utils and now we know whether a user has changed the image think about it the image can also already be there at the start when we first load the account with Google so this only has to happen if we re-upload the image so if that is the case we can say if has image changed then we need to get the image response by saying cons IMG res is equal to and now we have to upload it using a package called upload thing so we can do that at the top by importing something known as use upload thing which is a hook coming from at forward slash lib forward slash upload thing that means that we have to create an additional file right here within the lib folder which is going to be called upload thing dot TS and this file simply needs to contain generate react helpers it is a function used to generate the use upload Hook and the upload functions used to interact with the upload thing so you simply need to copy the code from here that's going to be under the docs for the uploadthing.com and then you can go to API reference react and then if you scroll down to generate react helpers you can simply copy this block of code but this upload thing that the S is also going to be within the GitHub just down below of course modify this with the add sign and then we have to add an additional file at the API route so we of course have to have the backend for image upload so now this means that we have to create a new file right within our app within the API folder so that's going to be API as well as a new folder called upload thing and then within it we need to have two files the first one is going to be core dot TS and the second one is going to be a route dot yes and right here if you go to next.js setup using the app router you'll be able to see this first steps that we have to make now we should always use the documentation when we have access to it so here they even provide us with a full example of how to create the core.ts file so we can copy this and paste it in the core.ts and then the second step is to create a next.js API route using the file router so let's simply copy that as well and paste it into the route finally we need to create a utils upload thing which is what we've done just now and finally use it within our file which we're going to do later on but for now I believe we're good I'm going to show you how to modify the core TS and the route yes to actually make it work so here we have our file router then instead of image upload we're going to switch this over to Media we have a function with an image of a Max file size of four megabytes as well as we're gonna add a Max file count equal to one of course there has to be a comma here then we have a middleware where we get our user but we're not going to be getting it from auth because you can see this is a fake auth action rather we want to get it from clerk so let's simply import something at the top and that something is going to be import ant current user coming from add clerk forward slash next JS then we can simply call it right here const get user is equal to async function where we simply await the current user and now instead of auth we can simply call a weight get user right here that's going to give us access the user which means that we are authorized and then we have the user ID being returned right here then on uploadcomplete we're simply console logging things and this is more or less it we have a function that's going to upload our files now we can also look into the route that we copied from their docs which is right here and here we simply have our file router coming from dot slash core the router file router this is more or less it this simply exposes whatever we created right here which is going to serve us for image upload upload thing was created to be really simple so we'll see that in action but of course the next step is now to actually use that upload button or the upload thing to upload images so let's close out of these routes upload things and let's move back to our account profile inside of which we're now importing this use upload thingy so what we can do here is we can actually use it and we're going to Define it right here as a hook by saying const start upload is equal to use upload thing and we're going to pass in a string of media inside of it and now we have this start upload which we can use right here on the on submit so if we have the image response rather we're going to get the image response coming from a weight start upload and we're going to pass in the files which is exactly one file since this is a weight we also have to turn this into an async function so we can say const on submit is equal to async function like this there we go this is the function block so this is looking good we're uploading that image and then we can check if IMG res image response exists and if the IMG res 0 dot file URL exists that means that we can actually update the values so we can say values dot profile underscore photo is equal to IMG res 0 file URL this is pretty cool with react hook form you don't have to care about you stayed and setting the state using the setter function you can just mutate the values like so and it's going to take care of things at the back end now this is great we now have the image and we are ready to submit all of the data that we are getting from our form so we're finally there here we're gonna have to call a backend function to update the user profile wonderful this finally allows us to close all of the files but remember where we were line 77 of the account profile and then get started with creating our backend pretty exciting stuff right let's clear our browser tabs a bit I know while we're developing we often have a lot of open tabs the only thing we need is going to be our figma which is not really important we have the finished website which is deployed right here and then we have our current developing website so we only need that and we are ready to get started with the back end and writing backend code in modern xgs 13 applications is a breeze let me explain why to give you a better idea why I want to use this interface where it allows me to draw some shapes so in general the majority of pages in our new nexgs 13 app are going to be server side rendered so server side rendered page right or at least we should strive to create the majority of our pages to be server-side rendered why do we want this because we're going to have SEO benefits increase loading times and much more now while we have server-side rendered Pages this doesn't mean that we cannot have any kind of client components inside of it so we can still have a couple of client based components like this within this server-side render page but the page itself is rendered on the server cool so why does this matter well before we had this entire server right let's call this a node.js server and this is it all the server code was here and here we could have different API endpoints so let's say this is API endpoint one we have API endpoint2 and finally API endpoint3 at least that's how it was before and then when we had a website we have of course a front-end or a client-side react app that looks like this that has of course many different components just as this one has so that's going to look like this and what we were doing is we were making calls to This node.js Server so we had some one-way calls which looks like this for example this component or let's put it this page was making a request to this API endpoint that looks like this so this API endpoint was let's say to fetch the user so we have fetch user and then this would actually make a call to our database let's say that this right here is our DB so this means that our fetch user would make a call to the DB which would then return the data over to the node.js server which would then ship it back to the client-side react application right makes sense this is pretty cool and this is how we have been developing the application so far but let me show you what we can do right now we can completely eliminate need for backend API routes why because we or the r on the server side so here we have different client-side components right this is a component one component two and if we were to make a request from a client-side render component to our page we would have to call a backend API and in node.js the way to create that backend API is to put it in the API folder and then create a specific route for example fetch user like so this is okay and you can do this for some specific things but a new paradigm that appeared in nextgs 13 and which we're going to teach in much more depth in our ebook and the upcoming next gs13 course by the way if you're watching this video and if you're liking it you're gonna love what we do there it's going to be like a 10 hour plus course with all the materials and everything you need to know to be a professional nextges developer so if you haven't already get the first chapters of the ebook down below and you'll also be notified once the course is launching so with that said let me still teach you all of that here as well if you had a client-side component you would have to make a request to the backend API but considering that we're already rendering everything on the server side the page itself which we're going to put as this box here can just make a call to whatever it can be a regular JavaScript function so no longer we have to have the node.js server with back-end routes because our entire application is server-side rendered so that means that none of this is going to happen essentially we can have a regular JS function which we can call an action so JS function action we simply make a call from our page to here and then we make a call to here and all of this time we are staying within our next JS application like this and all of it feels natural because we're simply writing JavaScript code at all times not creating additional backend servers I hope this makes sense this is pretty powerful and I do believe it's going to change and shape the way that the future web applications are developed great so with that said let me show you how we can create our first backend not route but action within the lib folder we can create a new folder called actions and considering that we're working with the users now we can create a new file called user.actions.ts here we can say use server this means that we're using server actions and of course you can read more about server actions right here they're in Alpha and this is going to allow you to Simply specify when specific code should be rendered only on the server pretty cool stuff now let's go back here inside of here we first have to make a connection to Mongoose which was that connection to the database that I was telling you about which means that we can create a new file within the lib and it's going to be called Mongoose dot TS inside of here we want to import mongoose from Mongoose we can say let is connected is equal to false and this is going to be a variable to check the connection status then we can export const connect to DB is equal to an async function like so and by the way I have a simple question for you you saw those diagrams that I made explaining you how the backend works if you're watching this please let me know in the comments down below if you want me to explain more stuff while I'm coding through those diagrams I can get a graphical tablet and explain more Concepts on a deeper level so if you'd like to see more of that please let me know if not just comment something else about the video great with that said let me show you how to create this connected DB function so we can say Mongoose dot set and we can set a strict query to be equal to true this is to prevent unknown field queries then we need to have a specific mongodb URL to connect to so if there isn't a process.env DOT mongodb underscore URL in that case we can return a console log that's going to say mongodb URL not found then we can check if we're already connected so if is connected in that case we can say already connected to mongodb and finally if that is not the case we can open up a new try and catch block and we can try to make a connection but of course to be able to make a connection we need to have an active instance of mongodb so let's do just that you can go to mongodb.com and click star for free then you'll need to sign up with Google or just create an account or sign in if you already have one after signing up you might need to choose where you want to deploy your database we're going to choose the free version which is going to be more than enough for our use case you can also choose a name for your cluster and you can click create now it's going to ask you to choose your username I'm gonna do JS Mastery and your password and then you can click create user don't forget to remember this password or store it somewhere and let's click create then we can start from our local environment or we can set up a cloud environment this is going to add you for some additional things so let's proceed with local environment add your current IP address but also we'll want to add all of the addresses so that we can access this API from anywhere so next you can add your current IP address and click finish and close great let's go to databases and then also allow access from anywhere this is going to make sure that you have less problems with deployment and the app is going to be accessible anywhere great now this is pending while it is pending we can go to database we can click connect and you can choose your drivers we want to choose node.js you have to install mongodb which I believe we have and then you have to add your connection string to your application so let's simply view the full code sample and that's going to look something like this the only thing we need in this case is the URI string so let's copy it and click close now let's go to our environment variables by going to env.local and there you can put your mongodb underscore URL is equal to this string right here of course you'll have to update your username and your password once you have your environment variable we are ready to connect the only thing you have to do is say a weight Mongoose dot connect and we're going to pass in the process.env dot mongodb underscore URL then we're going to set the connected to true and then we can cancel log connected to mongodb finally if we have an error we can simply console.log that error this is it this is the connection to Mongoose and now that we have it we can go back to user actions within here we can create a new function to update the user so we can say export async function update user and we can just say that this update user function is going to return a promise and it's going to be void just like so now inside of here the first thing we can do is call the connect to DB function which is coming from Goose and finally we can start making calls to our database but to actually modify our database what do we need to have well we need to have models so let's create a new folder right here within the lib folder which can be called models and there we can create a new user dot model dot TS and I hope you can see what I meant when I said that everything is Unified and everything in xjs is like a writing just JavaScript code no front end no back end everything is here user actions user model the app you don't even know whether you're in back in the front end because it all feels so natural of course at the start it's a bit weird because it gives you so much power so you're asking yourself hey should I be really doing this but the answer is yes I'm gonna teach you how to for the first time use nextgs 13 the way it's supposed to be used to be fully honest it took my team and I about six months to a year to properly change our mindset around how to write next.js13 applications because if you approach them as if you're approaching a react application that's not the right way of course it is react but you have to change your mindset and hopefully this course teaches you how to do that and then our Pro course even more so with that said let's create this user model by importing Mongoose from mongoose let's say cons user schema is equal to new Mongoose schema and then we have an object inside of it we can set the ID which is going to be of a type is equal to string and the required is equal to true we also need to have a username which is also going to be type string required true and also unique is going to be set to true as well then we're going to have a name which is going to be of a type is string and required is True Image can simply be of a type string bio can be of a type string and then most importantly we have threads so one user can create many threads which means that threads has to be an object of a type is equal to Mongoose that's Hema DOT type stat object ID and the ref to thread a string of thread so what does this mean this means that one user can have multiple references to specific threads stored in the database pretty cool stuff right after that we can have the onboarded status which is going to be type Boolean and default to false remember once we create an account we have to go through the onboarding where we choose our profile photo bio and the username and finally we have something cool which are communities one user can belong to many communities and a community again is a type Mongoose that schema that types that object ID and a reference to a community instance stored in the database great this is all that we have for our user schema so what we can do is we can export that user model by saying const user is equal to Mongoose dot models dot user or mongoose dot model user where you pass in the user schema why are we doing it this way well for the first time the Mongoose models is not going to exist so it's going to fall back to creating a mongoose model user based on the user schema but every second time we call it it's already going to have a mongoose model in the database so it's going to know to create it off of that instance and finally we can export default the user model now back in our user actions we can say a weight user dot find one and update and I didn't even see it but it automatically imported the user from data slash models forward slash user dot model now how do we update well this is going to take a couple of parameters let's try to see what each one does the first one we can pass an object of a specific ID so we can say ID is going to be equal to user ID and this user ID is going to come through prompts or rather params to this function so right here we can accept a user ID of a type string once we find the user we want to update we want to actually apply the updates so that's going to go into the second object of this function right here where we can say username is going to be equal to username dot to lowercase we always want to lower case it just to be sure and then what else do we want to do well we can provide all of the other fields of course username itself is also going to come through params so we can now put this into multiple lines so it's easier to see and we can also accept the username of course we're going to also have the name which is of a type string the bio of a type string the image as well of a type string and finally the path of a type string now we can pass those values in we have the username the name the bio the image and the onboarded status is going to be set to true finally we have additional options which is absurd of true and if you hover over the Absurd it's not really going to provide you a lot of information but a quick Google search is going to say that upserting simply means both updating and inserting okay makes sense so it's a database operation that will update an existing row if a specified value already exists and insert a new row if the specified value does not exist pretty cool to know great so this is it and then we want to Simply say if path is triple equal to forward slash profile forward slash edit then we want to also call a revalidate path and then pass a path so if you don't know what this one does let's try to figure it out together revalidate path is a nextgs function that allows you to revalidate data associated with a specific path this is useful for scenarios where you want to update your cache data without waiting for revalidation to expire pretty cool stuff so if you're updating the profile we can revalidate it and all of this should have been in a try and catch block so we do it in a try and then in the catch we actually just throw a new error so let's say throw a new error that's going to have a template string off failed to create update user and we're going to cons log the error Dot message as well error is complaining because it doesn't know of which type is the error so for now we can say it's a fit type any great and this is our function to update the user and let me ask you a question did you remember the line number that we have to get back to on the front-end side right now now that the backend is done I believe it was 77. so believe it or not that was it at least we have created the first action model and connection to Mongoose and mongodb so going back to components forms and then account profile I was right update user profile line 77. so now here we have to call await update user and this update user is coming from add forward slash lib forward slash actions forward slash user dot actions and what do we need to pass to it well we need to pass values.username values.name values.bio values.profile photo user.id and path name and this path name of course has to come from nextgs and we can get the path name because this is a used client so we can simply import right here a use path name as well as a used router and that is coming from next forward slash navigation we can utilize that hook right here const router is equal to use router as well as const path name is equal to use path name without any braces just like so and now we have access to the path name as well so we know whether we're updating or not now please allow me to point your attention to one thing well I guess that's why I'm here to point your attention to all of the things but this one in particular so if we open up the update user and if we put it side by side you can notice that we're passing one two three four five six params into this function and we're here passing Six params as well one two three four five six but keep in mind that they might not be in the same order so here we're passing the username as the first one the name bio as the second one and here the username is the second one and the name is third one and the user ID is the first one which is the second to last one right here so if we were to proceed with this all of our database values would be completely mixed up and it simply wouldn't even work because we would be passing username as the user ID and it will not be able to find a specific user by ID right so how do we solve this well of course we could rearrange it so that the values are actually in the right order but still you could make a mistake later on right here's a little Pro tip that I learned while developing these larger applications if you simply put everything inside of an object and pass that one object as the one and only parameter you're going to make your life so much easier because then you don't have to make sure that you're passing it in the right way or in the right order here it prompts you to specify the names of your keys and then you can give them the values so now we can pass the user ID username name bio image and all that good stuff and we can simply assign them specific values for example username is valuesad username name is values that name bio is values that bio and I think you get the point right image is going to be profile photo and path is going to be path name and then the user ID I believe is a user ID so now if we remove these values we're still calling the same function but it's complaining it's saying expected six arguments but got one so now we simply have to wrap this right here in an object which means that we're essentially just destructuring the first parameter that we are given and we can remove these typescript declarations from here and just put it like this now it's going to complain because it doesn't know which types these values are so to solve that we can say of a type we can say props or rather params in this case and then we can Define the interface the Rams which is going to get all of these values so user ID is a string username is a string name is a string bio is a string image is a string and path is a string as well user ID is actually going to be the user.id because it's coming from Clerk and we have to add commas to end this as well now we have no more errors but what we have achieved is pretty cool we made our applications less error prone why because now we're passing one object and what we're doing here is we're just destructuring the values from that object and a cool thing now is that we can pass them in any order that we want we can also mix and match here doesn't matter in which order they are because we're gonna pass the entire object and then we can simply destructure the individual properties from there by name great I hope you understand why this makes your code less error prone because we're much less likely to make a mistake here and also by using typescript we're going to know exactly through missing one field that we have to pass it's going to say immediately missing one field pretty cool thing once again if you like these little tips there's going to be so much more of them in the next JS course we're creating my team and I are really pouring our hearts and soul into creating that and making it the best next GS ebook in the next yes course out there so if you're loving this video that we're leaving for free You're Gonna Love what you're gonna see in there but with that said let's proceed we now have our update user we're calling that update user with the account profile and what do we do after we update it well we can redirect we can say if path name is triple equal to profile forward slash profile forward slash edit in that case we can just do router dot back to go to the previous page after editing but in this case we can say else simply router.push to the forward slash so we want to go from the onboarding to forward slash and of course let's fix the spelling here and I believe we should be good we can now actually take all of the values from the onboarding and pass them over to the user creation later on what we can do is we can go to the update user and we can simply console log the output because I'm just wondering what it's going to be to see that the user has indeed been created or we'll be able to see it within the database as well immediately so let me reload this page on the onboarding just to ensure everything is good even though we have a great profile photo here I will update it to ensure that the upload photo works as well so I'm going to go to downloads and put this JS Mastery Pro logo right here the name can be Adrian at JS mastery the username can be JS Mastery and the bio can be the real JSM and let's click submit before I do that I want to do a couple of things I want to open up the console here and expand it because you know we wrote a lot of code and there could be some errors so I want to just open up the inspect element the console clear it and just get ready for anything that the consoles throw at me so let's click submit okay we have the first one connected to DB and everything compiled successfully we have one error here upload think Dev server is working but we haven't set our secret in media's router config or uploaded the upload things secret so yeah we have to make that work as well so to make it happen we can go to uploadthing.com and click sign in or get started you can sign in with GitHub you can create a new project I'm going to call it threads and we don't have to pass the app URL and we can go to API keys finally we can copy the keys and paste them to our.env.local so let's simply paste them here below the mongodb URL now I believe that it should automatically reload our application after the change in the dnv.local and we can try to recreate the call that we've made before it looks like it pushed us right here but I'm not sure if the account was actually created this might have confused the database a bit but let's see what is the state I'm going to go to the cluster to the collections and then to users and we can see one user right here and it does look like there is some kind of a photo there so we'll see how that was but for now everything seems good the user is here this is great as you can see we're now logged into our application we have gone through the onboarding and if we try to Now log in with the same account we shouldn't be able to see that onboarding again it should lead us straight to the home page so let's test it out and it looks like I'm redirected to the onboarding again let's try it one more time I'm gonna say the real JSM and click submit there we go upload successful upload thing 200 as well connected to mongodb and we are back at the home page now it says simulating file upload clerk to request upload thing is redirected because there's no signed in user and the pad is not included in the ignore routes I believe this right here is just a little helper from clerk telling us that we can further improve our application for now we can keep it as it is and then if you notice any issues later on we can get back to fix it but with that said we are back on our home page we're logged into our personal account and we are ready to create some posts so finally we can create a create thread page and we can get started with creating some posts because we cannot read the posts if we don't yet have any so let's go to app Root and then right here we can create a new folder called create thread and a new page.tsx within it inside of there we can do a simple async function page and we can also export default Dot Page at the bottom inside of there we can return an H1 that's going to say create thread and this can have a class name equal to head Dash text so if we now save this go back you can now see a create thread page also the active status on our nav item switched and we can now move between the two great so what are we actually going to have inside of here well first of all we want to know which user is currently creating the thread so what we can do is we can immediately get some useful things from Clerk and that useful thing we have already used before it's going to be import current user coming from add clerk forward slash nextgs while we're here we can also import the redirect functionality coming from next navigation and then at the top we can say const user is equal to a weight current user if there is no currently logged in user we can simply return null so if no user we can say your return null I believe clerk would also redirect us automatically if we're not logged in so that's pretty cool thing as well but if we do have a user we can fetch his data from the database by saying const user info is equal to a weight fetch user where we pass in the user ID so this fetch user is another user action that we need to create so for now we can collapse our current update user and just below it we can create a second one which is going to be a fetch it's going to be much simpler so we can say export async function fetch User it's going to accept only one parameter of user ID which is of a type string it can have a try and catch block we want to connect to the database first at the top and then as soon as we connect we want to Simply return a weight user dot find one there we pass the ID as the user ID and then we call a DOT populate method on it and there later on we'll want to populate the communities right now I believe we don't have any communities connected to that user but let me show you if we space it like this the path to the populate is going to be communities and then the model is going to be community we don't have access to this yet but we will have it soon so for now we can simply comment this out and then we can throw a new error that's going to say fail to fetch user with the error message of a type any if something goes wrong but for now we're simply returning that user from the database this is important because later on we'll know which communities does that user have access to great now we can collapse this function as well go back to the page and import it from add forward slash lib actions user actions well what we can do is we can make a check if no user info question mark dot onboarded so if it's false then we want to redirect to the onboarding okay so this is going to allow us to move all the users that maybe switch their URL bar manually we're going to bring them back to the onboarding if they haven't onboarded yet at the same time this is a good check for us as well to see if we're properly onboarded so if I reload the page here and if we're still there means that we are good and once we have all of that info we can now return not only on H1 but also a new react fragment that's going to look like this and then within it we can have an H1 but also we can render a new component called post thread which is essentially going to be a form and to it we're going to pass a user ID equal to user info dot underscore ID now of course we don't yet have access to this post thread component it's going to be one of our form components so we can create a new component within the form components and it's going to be called post thread dot TSX inside of there we can create a new function post thread this is of course functional component and it's going to accept the user ID as the first and only prop so we can say this is going to be equal to user ID of a type string now inside of here we can immediately return something so we can say return and later on it's going to be a form but for now it can be an H1 that's going to say post thread form and save and of course export default post thread there we go so now if we go back we can import it coming from add forward slash components forms post thread and now if we save this and reload you can see if you hover over it post thread form which means that now we are ready to dive into the post thread and start creating the second form of our application we have the first one for the onboarding but now we're going to have the second one for posting a thread and I'm going to show you that a lot of things are really similar once you understand how Zod works and how react hook form work the majority of things is going to be pretty pretty similar as a matter of fact so similar that we can go back into a previous form and we can immediately just copy some stuff from here I'm going to copy the entire top part so everything from use form right here including all of the Imports and everything that we had from before so everything from the end of the use form all the way to the top I'm going to copy it and paste it right here above the post thread right here now we can remove this const account profile and move the actual States within this functional component right here there we go of course we're going to switch this later on now let's look into the Imports that we have here which ones do we need for this form as well and which ones can we get rid of well first of all we will need everything from Zod so this is good we will need the use form from react hook form so those two are looking good to me we will need a button as well as the majority of things from the form components coming from shot CN there's one more that we're going to need which is called a form message and I believe we missed the form message in the previous form we also needed it there but I think I just forgot to add it the form message is the error message that appears if something goes wrong so we definitely want to add the form message within the account profile as well and then we want to add it as a self-closing component below every single form control so you have a form control here we want to add a form message as a self-closing component this is going to show the error message and we can repeat that procedure for all four of our Fields right here there we go just to be sure now we can go back we have the form message and everything we need in this case we won't be needing an input but we will be needing a text area the Zod resolver has to be there later on we're not going to have a user validation but rather a thread validation so I'm going to put this at the bottom because we're going to switch it later on to our new validation the use router and use path and we're going to come in handy but we won't be using the upload thing nor the base64 nor the change event and use state from react nor the image so we can remove these four the update user is definitely going to be switched it's going to be update thread or create thread later on so for now we can comment out these two and as you can see a form is a form right you're going to notice as we create more of these forms they're always going to follow specific structure importing the use form Hook from react hook form a button to submitted as well as the form fields and text area inputs and then Zod for validation now we won't be having state in this file and we don't have to worry about images so we can remove that the form is also going to be a bit simpler later on ADD thread validation here and then the default values are going to be simpler we're simply going to say thread is an empty string and then the account ID is equal to user ID which is coming through props and while we're here why not immediately do the thread validation so we already know how to do it so we have to go to that's going to be validations and create a new thread dot TS within the validations folder we can copy this user validation paste it into the thread one and then there we can rename it to thread validation Z dot object and then we're going to have a thread which is going to be a z dot string Dot non-empty and then finally dot min that's going to be 3 and we can put the message there of minimum three characters of course you can add the maximum as well but this is good enough for now and then the second field we need is the account ID which is of a z dot string so this is just ensuring that we pass all the necessary Fields into our form and later on we're going to also be working with comments so while we're here we can create a comment validation because it is incredibly similar to this one it's also going to have a thread validation because every comment is a thread of its own so now with that said we can go back to our post thread and at the top instead of user validation we can import the thread validation coming from validations thread great and now we can use it right here within the Zod resolver and we are ready to create a form now how is our form going to look like well forms also are quite similar so what we can do is we can go to our account profile and we can copy the start of the form which is this part right here right and we can paste it into the post thread right here let's expand our return of course we have to close both of the forms just like so and then we can also copy one of the form Fields let's do a second one here for the name we can copy it we can paste it right here within the form in this case it's going to be a form control with a name equal to thread for slower case it's going to return a form item the form label is going to say content of the thread we do have a form control then and it's going to render a text area instead of an input and we can give it a property of rows which is equal to about 15 maybe instead of giving a class name to a text area we can give a class name to this form control that's going to be no Focus border border Dash dark Dash 4 BG Dash dark Dash three and text dash light dash one this is going to make it pop and finally we must not forget an on submit Handler so we can say const on submit is equal to a function that's going to look something like this now if we save the post thread we should finally be able to see our create thread page and there it is you can see create thread content is here and then we have our actual text area of course we can apply some additional class names to this such as Mt 10 to divide it a bit from the top and that's going to make it look like this where we have the content and then we have the actual form field but of course what is missing from this simple create thread form well a button right at the bottom of this form we need to have a simple button so it's going to be still within the form we're going to use a shot CN component that's going to have a type equal to submit which is going to call the on submit Handler and the class name of BG primary 500 and now we can simply call the Post thread if we save it the button is here and now once we click it it's going to call this function so now we have to implement the logic for creating a thread pretty exciting right I mean we've came to the creation of the post really soon after the creation of the user you're going to start notice how now that we have the layout and the general structure of the application as well as the general database connected and all here set up within the actions models Mongoose and all that good stuff it's all going to become so easy and second nature because for every single new model and every single new action that we add we can simply reference the current structure that we have so with that said to implement the on submit we simply have to call a weight because this is an async action so we can turn this into an async and we have to call a weight create thread of course this is a backend action which we can create next so let's go into not any more user actions but rather you know the deal we're going to create a new actions file called thread.actions.ts and then within thread actions we can create a new export async function create thread that is going to look like this of course it's going to accept some parameters but let's not pass them one by one as we said before but rather as an object of a type props or rather in this case it's params okay so let's define those params at the top interface params and now we can Define it what does our thread need to have it's going to be text of the thread which is a string an author of the thread which is a string as well Community ID if it belongs to a community which can be a string or a null and then a path which is going to be a string we can now accept those props right here by saying that we want to get a text we want to get a author we want to get a community ID and the path and I do believe all of this fits nicely in one line as well there we go so now we can actually open up this function block and the first thing we need to do is connect to DB and you simply call it as a function and import it from data slash Mongoose and then we can create a thread we can save it in a variable that says const created thread is equal to a weight thread dot create but hey what is this thread think about it for a second we had a similar thing with user right here we had user dot find one or user dot create so what is the thread well it's a mongoose mongodb model which we haven't yet created so let's go into models and create a new thread dot model dot TS and we can now copy the user model paste it in the thread remove the majority of these fields or all of them as a matter of fact rename this from user schema to thread schema rename the Mongoose model from user to thread right here and to thread right here hand to thread right here as well please ensure that nowhere on this file the word user is mentioned at this point this is really important because if you don't copy everything that we have right here thread schema thread schema here thread thread thread thread it's not going to work because it's going to think you're creating the user so what does our thread have well it has the text which is of a type string and required to true it's going to have the author property which is going to be this time a type of mongoose that schema that types that object ID and it's going to be a reference to the user and the required also is going to be set to true then it's going to have a community and the community is going to be of a type is equal to mongoose.schema.types that object ID and the ref is going to be community it's going to have a created add which is going to be of a type is date and the default is date dot now it's also going to have a parent ID in case this thread is a comment which is going to be of a type is equal to string okay some pretty cool stuff is happening here and then it's going to have children children is going to be an array where we're going to have an object that is of a type once again mongoose dot schema dot types dot object ID but this time a reference to itself okay what does this mean this mean that one thread can have multiple threads as children okay and what we're doing here is recursion so we have one original thread let's call it thread original and then somebody can post a comment on that thread so thread comment one somebody can pose the second comment thread common two but then that comment itself somebody can comment on that as well so then we're going to have parent of these two children and then this is going to become the parent itself of this child right here so it's a multi-level commenting functionality which is pretty cool great now what we can do is this is it we can export This Thread schema in the thread model and we can get started with using it right here within thread actions we can import thread from models thread.model and then what do we need to pass to it well it's going to be text author and Community now this community is sometimes going to be a community ID if that member is a member of a community or it's simply going to be null so we're going to figure that out later on for now we can simply leave it as null it's going to be cool to come back here and then implement the communities after all once we have the basic user creation functionalities Now once we have created a thread we also need to update the user model so we can say update user model or rather that instance of a specific user by saying a weight user dot find by ID and update where we have the author and then we can update it like so by pushing threads created thread dot underscore ID and of course the user is coming from Models user that model so it's not only enough to create a thread but we have to push it to that specific user who created it okay because if you look into the models of the user the user indeed has access to references of different threads that they created great and finally we want to revalidate the path so we can call revalidate path and pass in the path which is going to make sure that the changes happen immediately on our next.js website so for now this is it later on we're going to add the community functionalities but for now users can only create their own posts and we can go back to our page or rather to our post form and we can now actually call the create thread which is coming from add forward slash lib actions thread dot actions so please import it and once you do so you'll need to pass all of the values to it so the values that we're going to pass are going to be text which is of a value values that thread not get random values but other values that thread we can expand this and we can check what else do we have to pass it's going to be text author Community ID and path so let's simply paste all of these here the text as we have already said is going to be values.thread and the question is where are these values coming from and the values are coming right on our own submit because it has been created by react hook form so immediately here we get the values and those values are of a type Z dot infer type of thread validation so we immediately know what type is that off and this is type off there we go so now we have values.thread the author is going to be user ID the community ID is going to be null for now so we can say community ID is null and then path is going to be the path name let's see how we defined it here there we go path name so now we're passing all of the necessary values to create a thread and once we create a thread we want to move over or rather use the router.push to push to the home page or the forward slash to see our great newly created post now it seems like we have an error saying cannot read properties of undefined reading thread at Mongoose that models.thread so let's see what that is about we can go to our models thread.model and here it's Mongoose Dot models.thread so apparently it's saying that the models is undefined so it doesn't allow us to get the models and throws an error but that shouldn't be the case because the model should always exist on the Mongoose object it is an array containing all the models so let's see where we're calling this object we're calling it in the create thread actions so if we go to our actions thread actions we do call connects to DB at the top which is referencing to our function that's looking good we call create thread thread.create we pass all the values we update the user and then revalided a path this is looking good to me but what we can do is we can wrap this in a try and catch block as well so let's do just that all of these things should go in a try so we're trying to make it work and then if it doesn't work we can get a more meaningful error message if we catch it so that's going to be throw new error and we can see something like error creating thread and then we can conserlock the error Dot message and we can leave the error as type any now if we save this we're not even trying to execute this the error happens before if you think about it because we're not even calling the create thread function the error happens immediately within the thread.model.ts which is a bit weird if you think about it because we have the same structure within the user model right we have user Mongoose that models.user here we have thread Mongoose that models.thread so let's think about why we're getting an error right now let's see where our thread.model.ts file is imported so we can search for thread.model.ts and it seems like it's nowhere but maybe we can just search for forward slash thread and we can see that it's being imported right here and this is within thread actions and do the thread actions have any differences from our user actions if you pay close attention you're going to notice one difference and that is that here we use this use server directive and here we don't so at the top we can say use server and that should fix the error there we go now why did this happen well that's because we can directly create database actions through the browser side one reason is course cross origin requests it doesn't allow it that's why we develop apis and new servers databases are mostly server or API services so this simple next.js directive makes it all work and this means that now we can post our first thread let's say something like nextgs 13 is great especially server components and use server directive and let's post it we got redirected to the home page which is good but now the final question is was our collection of a thread created so let's try to just reload it by clicking refresh here and you can see that we have threads and within the thread we have a text next.js13 is great and we have a community of null children of array and we have our author this is great let's see if this author ID corresponds to our ID and here with the users it is the same one so this is great our first thread has been created and this means that we can actually fetch it within our home page so now we can close all of the currently open files collapse it go to app Root page.tsx and then we can start implementing the home page by finally fetching our threads so let's do that next inside of our home page we can start by fetching the posts so how is that going to look like well you know the drill we can do something like a weight fetch posts which is our server action and this is going to return a result so we can say const result is equal to a weight fetch posts now of course we have to turn this into an async function so we can say async function home and now we have to create This Server action so let's do that next you know the drill let's go to the lib actions thread actions and then now on top of our create thread or rather on the bottom of it we can create our fetch threads or rather fetch posts how we call that right here but either way is fine so that's going to be export async function fetch posts or threads but once you do one make sure to stick with it and we can pass two different things to it a page number which we can set the default by one as well as a page size which the default can be 20. and that is it as you know the drill we have to connect to DB first so we make a call and then we can just start finding the posts so we can say const posts or rather posts query is equal to thread dot find and now here we can pass the requirements of our search so here we want to fetch the posts that have no appearance and these are top level threads so threads that do have parents are common and we do not want to find comments we only want to find real threads so the way we do that is we say give me the threads that the parent ID is in and it's going to be either null or undefined there we go and then we can further sort it skip it or limited let's start with that sort and we can say created add is going to be descending which means that the newer ones are going to show up first then we can Implement pagination as well for the pagination to work we need to know on which page are we on so we need to calculate the number of posts to skip depending on which page we are on so we can say const skip amount is equal to page number minus one because the first page is going to immediately show the posts and then times page size so this is going to skip 10 20 or 30 to only give us the next page so we can now use this by saying dot skip amount then we can dot limit to the page size and then we need to populate so what we're going to populate is going to be the path of author and the model is going to be user so we want to populate with the user model we also want to populate with something else so we can recall the dot populate and we can pass a path of children so this is to ensure that we have any comments this is the path but now things are going to get just a bit more complicated we're diving into a bit of a recursion here we have path but then we can again populate choose what they want to populate the path of the author the model of the user and we want to select the underscore ID name parent ID and image so we only want to select specific things because we don't need to share everything considering that this is a children thread or a comment so we're populating the Creator the children which are comments and everything is good now we also need to know the total posts count because we're going to need it later on for the total number of pages so to know that we can create a new variable total posts count is equal to a weight thread dot count documents and we also need to count documents in the same way that we did before where we only want top level documents not comments so we can take the parent ID right here and put it within it then we can say const posts is equal to a weight post query dot execute or exec and then we can say do we have a next page so const is next is equal to if total posts count is larger than skip amount plus post that length that means that we do have the next page so we can return an object containing all the posts for that page and is next I know that this is a bit of a more complicated fetch post function but we're immediately implementing the pagination right here great now we know that we have to pass two parameters to it page number and Page size so going back we can call this fetch posts we can import it from add forward slash lib actions thread actions and for now we can hard code the page which is going to be one as well as the total number of posts which can be 30. later on we're going to make this work dynamically with pagination now let's see if we actually are getting our posts by console logging them right here console.log result and let's check it out now if you open up the console right here you're going to see something weird and that is that the console log is not happening even though we are on the correct route on the home page that's because we don't have a use client directive right here at the top which means that this is rendered on the server as we discussed before so the console log is going to appear here and we indeed do have one of our posts which is great so now we can actually ensure the render it on the screen which is what we have been trying to do this entire time So Below the H1 let's create a new section and that section is going to have a class name equal to mt-9 for margin top Flex Flex Dash coal so they appear one below another and the gap of 10 to provide some space then we want to see if results or rather result dot posts dot length is triple equal to zero meaning there's no posts in that case we can return a P tag saying something like no threads found and this can have a class name equal to no result but on the other hand if we do have some posts we can render another thing which is a react fragment and there we can do result dot posts dot map where we Loop over each individual post and return a thread card for each one of these posts and while we're here even though we haven't created it yet we already know what we have to pass to it first we have to define the key which is equal to post dot underscore ID then we have to provide the ID just as a prop as well pose that underscore ID we need to also provide the current user ID which is going to be equal to user.id and this is again coming from clerk we've already used this before so you know how it works we have to import the current user coming from add clerk forward slash next Js and then right here we can say const user is equal to a weight current user so now that we have the user we're also passing that user.id and we can put a question mark right here just to ensure that the ID actually exists then we can pass a parent ID which is going to be post dot parent ID although it should be null in this case because this General post doesn't have a parent it's not a comment we need to pass the content which is the post dot text the author which is the post.author the community which is the post dot community created at which is the pose.created ad and comments which is the post that children and now we can go ahead and create this thread card so it's going to be a new component this time within components and then cards it's going to be called thread card dot TSX inside of here we can create a new const thread card which is going to be a functional component and we already know that we're getting some props so which props are we getting well all of these right here so you can hold alt or I believe it is option on Mac and you can copy all of these and then press Ctrl C or command C to copy it and you can de-structure them right here from prompts and of course we have to provide commas after each one there we go now we have to say that this is of a type is equal to props so we need to define the interface of props right here at the top by saying interface props and then we need to Define what each one of these types is so the ID is going to be of a type is string then we have the current user ID which is also a string we have a parent ID which is either a string or a null as we discussed before the content is going to be of a type string author is going to be more complicated it's going to be an object with a name equal to string an image equal to string as well and also an ID equal to string by creating these specific types we're going to immediately know what the values do we have access to when we read those props then I notice that here I should have put this sign right here to say or and then after that we have community community is also going to have an ID of string a name of string and an image of string or it's going to be null so maybe we don't have Community if it doesn't belong to one created that is going to be a string and comments is going to be an array so we can say comments and comments is going to have an author which is going to have an image of a type string in this case we don't yet have to show the actual comment we just have to show who the commenters are because this is just a general thread card and not the thread Details page where we can read all the comments and then we can add the array sign here to indicate that we're going to have multiple comments and then finally is comment which is going to be a Boolean and it's not required so that's going to look like this and now we can collapse this so finally now with that we're getting these props we can return an article again this is like a div but it's an HTML5 semantic tag which we usually use for cards immediately let's try to show some content the content we can show is the text itself so we can render maybe an H2 give it a class name equal to text Dash small Dash regular and x dash light Dash 2 and within it we can render what it's going to be content of that specific thread now if we save this and go back of course we have to import the thread card and to import it we have to first export default thread card and then what we have to do is we have to import it right here which is coming from add forward slash components forward slash cards forward slash thread card now it complains a bit saying cannot find thread card even though we imported it at the top but it looks like this is good so now the error is gone but it's complaining about the current user saying that this could potentially be null interesting typescript is trying to save us here so let's see what we can do well we can say user ID or it's going to be an empty string so this way it's always going to be a string that's getting passed in and there we go we can see one of our first posts nextges 13 is great especially server components and use server directive this is great we can close the additional tabs that we have opened as well as the console I might as well update my Chrome but the point is we can now get started with creating the layout of our card and everything is slowly starting to make sense so let's continue with the card structure first let's provide a class name to the article itself which is the actual card and it can be a flex container W Dash full for full width Flex Dash coal so the elements appear one on top of another rounded Dash XL and BG Dash dark Dash 2 as well as a padding of seven now if we save this and there we go so now it is an actual card but let's make the content inside of it look even better so right here within the article we're going to create another div that's going to be a container of flex items Dash start and justify Dash between inside of it we can have another div that's going to have a class name equal to flex W Dash full flex-1 Flex Dash row and gab Dash 4. and inside of it finally we can have another div which is going to contain a link that's actually going to be an image the profile photo so it's going to have a class name equal to flex Flex Dash call and items Dash Center finally now that we have the structure we can put a link within it and this link is going to come from next forward slash link of course every single link needs to have an href property and this time it's going to be dynamic so we can say forward slash profile forward slash and then author dot ID so it's going to link to the profile of the creator of the thread and it can have a class name equal to relative h-11 and w-11 for height and width finally within it we can render a self-closing image tag and image is of course coming from next forward slash image let's give it a source property equal to author dot image and I'll tag equal to profile image fill and then class name equal to cursor Dash pointer as well as rounded Dash full as profile images usually are now if we save this I reloaded the page and there we go so already this is looking much better but now below this link we can render a self-closing div and this Dev is going to have a class name equal to thread Dash card underscore bar so now if we save this it just provided some more space what we can do is we can check out this class name and what it does by going to search and then here you can see it's just going to apply margin top grow rounded full and a neutral background now below the div we created and Below one more div we're going to create another div and here we want to render the author's name so right here we can render a link property and it's going to point to the same link that we had before so we can copy this entire link and paste it here href profile author but we don't have to have the relative class name here it can simply say w Dash fit for the width and within it we're going to have an H4 and for now we can simply render the author dot name and it's going to have a class name equal to cursor Dash pointer to make it appear that we can click on it to go to the profile page text Dash base Dash semi bold as well as text dash light dash one so now if we save this you should be able to see Adrian appear right here as that is the name of my account and of course we have to make it make sense with the image so let's give it a class name equal to flex W Dash full as well as a flex Dash call so now if we look at it it's going to appear more nicely with the other content that we're going to add around it so now we go below the link and we have a P tag and this B tag is going to render the content of the thread so we can give it a class name of mt-2 to divide it a bit from the top text Dash small Dash regular and text Dash lite-2 and considering that we now have our content we no longer need our H2 that we had before so if we now save it you can see it more nicely fits inside of here now below that P tag we can create another div and that div is going to have a class name equal to mt-5 for margin top Flex Flex Dash call and gap-3 inside of it we want to have some usual social media icons such as Hearts reply repost and so on so let's simply create a div that's going to contain all of these icons and this div is going to have a class name equal to flex and GAP 3.5 inside of there we can render our first image or rather an icon this image is going to have a source equal to forward slash assets forward slash heart Dash gray dot SVG it's going to have an ALT tag equal to Heart a width equal to 24 as well as a height equal to 24 and a class name equal to cursor Dash pointer and object Dash contain now if we save this you should be able to see our heart icon appear right here and now you can see this div which is actually a line pointing down this is going to indicate that we can have later on more comments attached to this thread which is what threads is all about of course I'm talking about this thread card bar this is adding that line right here now let's duplicate this image three or four times so we can show some other stuff as well the second one is going to be the reply so we can say assets and then reply change this to reply as well then we can have repost below that's going to be the third one and then finally we can have the share button now if we save this you can see four different icons appear right here the only one that's going to be working in our case is going to be the comment button so we can wrap the second one the second image within a link tag so let's do a link and put this image within that link and indent it properly this link is going to have an href pointing to forward slash thread forward slash ID so it's going to point to The Details page of that thread so we can then comment on it and nothing is going to change visually this is great of course if you want to I have an extra challenge for you if this video isn't complicated enough implement the like functionality heck implement the repost and share functionalities as well that is what JSM is all about empowering you to build your own projects now with that said below our thread or rather below this div containing all of these images we have to figure out if the threads we're rendering are comments because as we said a comment is a thread and a thread is a comment but we have top level threads which are general ones right and then we can have common threads attached to it so we have to figure it out and how can we do that well we are passing the thread card and there we have the ID comments and all that good stuff but we don't have a specific is common functionality like I believe we said we will have right here is comment so by default this is going to be undefined because we're not passing it at all so let's simply specify it is comment and at the start it's going to be undefined but if we render This Thread card later on as a comment we can then pass this variable and say that it is true so let's go down and say if is comment and comments.length is greater than zero only then do we show this section and that section is going to be a link that's going to point to the same one that pointed here thread details right here and what do we put in there well we can put a P tag that's going to say comments dot length and then we can say replies which is going to look like this now of course we have to give it a class name equal to margin top 1 to divide it from the top text Dash subtle Dash Medium as well as text Dash gray dash one if we save this you should now not be able to see anything but we will soon enough we'll also add additional functionalities so you'll be able to see the photos of the people that replied as well as the number of replies and then even when the post was created alongside the community it belongs to so some pretty cool stuff is coming our way but for now I believe this is good enough for our thread card so now let's go ahead and go to create another just to see if everything truly works I'm gonna ask Chad GPT to write me a Tweed about the power of next 13. and there we go it gave us something cool let's copy that paste it here and let's without even reading it post it I guess that's how the people in the AI Revolution just do social media and now you can see that it didn't automatically show up here so we have to reload and I believe it's going to work immediately on the deployed version but no worries we're gonna look into that later on so far this is great we have two of our posts so the next thing we can do is the thread Details page which we don't have as of now so we can go to the app and then root and then create a new folder called thread and within it create a new folder with square brackets ID and then create a new file within it called page.tsx that's how we do Dynamic routing in xjs because we want to read this ID at the top and we can start creating that this is going to allow us to add comments to it which is going to be another interesting functionality which we can add so here we can say const page is equal to and this is going to be an error function which we can immediately export default then we know we're going to get some params out of it and in xjs how do we get the params out of the URL well you do it like this you destructure it you get the params and within the params you're going to have the ID so we can say that the type of this is going to be params where we have an ID of a type string this is it so now this allows us to start creating the structure of our thread Details page so let's create a section and this section is going to have a class name equal to relative and within it we're going to have a div and within the div believe it or not we're going to render our thread card which we already created so we can go back to our home page and copy the thread card and we can paste it right here within the div and of course we want to format this properly and indent it so I'm holding alt and then clicking and now I can move it all right here now of course we have to import the thread card component which we were working so hard on to create but of course we also have to get those post details or the thread details so how are we going to do that well we can immediately start at the top we can say if no params dot ID we can return null if we do have the params ID we also need to have a user and you already know the drill how do we get the user while we import current user coming from add clerk forward slash nextgs then we can say cons user is equal to a weight current user and if we don't have a user we can again return null then we have to turn this into an async function since we're using a weight which is pretty cool that next JS allows us to do it like this and then we also have to fetch the user data from our own database so we can say const user info is equal to a weight fetch user and this is the function that we have already created coming from lib actions user actions and we can pass in the user dot ID why are we doing this well we're doing it to check if user info dot onboarded is true so we can say if no user info question mark that onboarded in that case we want to redirect to onboarding or rather to forward slash onboarding and it looks like I'm missing a return statement right here so we can say return this entire section this redirect is coming from next navigation as well so we can import it right here at the top but now you might be wondering where are we going to get this post from or as a matter of fact it would be better to call it thread so I'm going to modify all of this into thread that those things where are we going to get this thread data from how are we going to know on which thread are we currently on well for that we're gonna have to do something like this cons thread is equal to a weight fetch thread by ID and we have to pass the programs.id which we're getting right here at the top from the URL bar of course this function doesn't exist yet so we can go back into our thread actions right here and we can implement the third action in a row which is going to be fetch thread by ID so export async function fetch thread by ID and of course is going to accept an ID of a type string at the start we have to connect to DB a well-known process by this point then we open up a try and catch block and we try to fetch a thread by saying const thread is equal to a weight thread dot find by ID and we pass in the ID then we need to populate it by saying dot populate we need to give it the author information so path is equal to author model is equal to user and we can select which Fields do we need from the author it's going to be underscore id id name and image later on in here we'll also want to populate its community so for now I'm going to add a to do here for myself populate community but what we can populate now is going to be the children which are the comments and this does get a bit complicated let me show you why we can say dot populate and then we have to go a couple of levels deep we first populate the children and then we call another populate that's going to be an array where each child meaning each child thread gets populated with the author of that specific comment right a model that's a user for each one of these we want to select specific properties like underscore ID name parent ID and the image and then we want to populate the thread comment itself so we can say path is children model is going to be thread populate is going to be author model user and then we want to select id id name parent ID so this is getting complicated right we need to do this because we have many many levels deep and we want to know that is a common to another thread so as I said it goes recursively it's going to make much more sense once you see it in action but we can also see it a bit on a finished site here we have a thread right we have to get the data for that thread but once we get the data for the thread you also have to fetch the profile icon for the sub comments as well as the comments of the comments here we have a reply on a comment so this is getting complicated but it is a multi-level commenting functionality which is pretty cool so take some time try to understand this we're going to go through this later on once we actually implement the commenting functionality and finally you can call the dot exec which is execute on this specific query and then you can return that thread in case there's an error we can simply say console.error or throw new error error fetching the thread and then console log the error message and make the error open any type and this is it when it comes to our fetch thread by ID functionality but we must not forget this populate Community as well because we have to fetch the data for the community of this post as well otherwise we're not going to have it on the front end now with that said this function is now created and we can import it from add forward slash lib actions thread actions which is great and now we are passing that data over to our thread card so now if we go back to our website as you can see the first part of the thread Details page is now done because it is essentially just the post itself which we already have on the home page but we had to do that we have to start somewhere right and now the main part of the post Details page are the actual comments not only the ability to see the comments but the ability for you to add a reply so let's add that next below this div we can create another div and this Dev is going to have a class name of mt-7 meaning margin top 7. to divide it from the top and within it we're going to render the comment self-closing component so we can go to our components this is going to be actually a form for our comment our common form so we can say common dot TSX and there we can create a functional component you can do function comment like this or you can do a regular const comment there really isn't any difference and you can do export default comment at the bottom now for now this comment can simply return something like a div where we can say H1 comment form and of course we have to give it a class name equal to text Dash white just so we can see that right there and now we can import it right here from comments forms comment and on our website you can see this common form to this comment form we'll have to pass a couple of props we first have to pass the thread ID to know which thread are we commenting on so that is going to be thread ID is equal to thread dot ID then we want to pass the current user image which is coming from user dot image URL why do we have to do that well to show it right here and this is thankfully coming from clerk so that we know that we are the one currently commenting and finally we have to pass the current user ID which is going to come from the database user info dot underscore ID and I believe that the underscore ID is not really a string it could be a special mongodb object so just to be sure I'm gonna wrap it in a json.stringify function and that way we know that we're working with a string now we can move into our comment and we can accept all of these props within it so we know that we're going to get a thread ID a current user image or IMG as we passed it and a current user ID and that's going to look like this of course that's going to be of a type props we can pull our editor a bit here so we can see more of what we're building as a matter of fact we can move this here because we can still see the comments in our mobile view now we can define those props as an interface at the top by saying interface props thread ID is a string current user image is a string as well as current user ID is a string as well and we are ready to start creating our form it's not our second form I believe it's a third one and all of them follow a similar structure so you already know what you need to do but let's do it together one more time when we were creating the second form we referenced the first one now we're creating the third one and we can reference the second one which is the form thread one so from here we can get almost everything I believe the button the form so let's first get the entire jsx part of it which is just a form right and we can move that form to the comment right here of course it's gonna complain that not a lot of imports are here but no worries let's continue getting some more things such as the on submit as well as the form the router and the path name so we can put that right here above the return and then we can get all the Imports as well so we can copy the Imports as well as the use client because we're going to need it whenever you're working with forms you're going to need a use client because it's a browser event and now we can paste it here now we won't need all of these things and some imports are going to be different but let's take it step by step we need Z from Zod because that's for our validation we need the use form hook in this case instead of a text area we're going to need an input so we can simply get an input over here we need a button but we have it here already now instead of a thread validation we're going to use a comment validation but if you remember we have already created it they're so similar so we should already have it in our thread.ts file and then of course we won't need create thread later on we're going to need the add comment to thread functionality but more in that later so for now we can replace this with comment validation in this case we don't need the account ID here we can also say comment validation and we can comment out the part of the on submit and as you can see we have a text area here but here we want to switch it to an input that's going to have a type equal to text and we don't need rows here but we do need a placeholder that's going to say comment dot dot and we can give it a class name equal to no Focus text dash light dash 1 as well as outline Dash none now if we save this I hope we'll be able to see our form and there it is it's quite similar to what we had in the create thread but of course we're going to modify it so it looks like this we want it to be horizontal of course the profile image on the left the comment form in the center and then the reply button on the right so let's go ahead and modify the jsx software form to suit our needs we can do that by giving this form a class name off simply comment Dash form already this is going to make it look a bit closer but now we can modify the form field the form field is going to say thread because think about it we are creating a thread then instead of the render we're going to have the form item inside of the form item we're going to have a form label and then inside of the label we're going to have an image image has to be imported from next forward slash image and we can give it a source equal to current user image this is coming from clerk we can give it an ALT tag equal to current user or just profile image whatever you prefer a width of 48 and a height of 48 as well finally a class name of rounded Dash full which we need whenever we're working with profile photos and object cover now if we save this you can see our JSM logo or your own profile photo which is pretty cool and now we have the form control this form control simply can have a border Dash none as well as a bg-transparent and now if we save this you can see it's looking a bit closer but still it feels like it's not fitting in here so let's figure out why is that in this case we don't need a form message so we can remove it and in the form item we don't need Flex call because we want elements to appear one next to another and then we can also say items Dash Center if we save this as you can see we're getting there and we can remove the class name from the form label because we don't have text inside of it we just have the photo which doesn't need any additional styling and let's modify the button it's going to be type submit and we can give it a class name of comment Dash form underscore BTN this is going to give it this special look and we can simply say reply this is going to make it fit in one line and there we go this is it it's looking exactly like it does on the finished version so that means that now we are ready to leave a comment or are we of course this is now the part where we have to go to the on submit and we have to create the backend functionality for creating a comment so let's go to our thread actions and yes we are still in the threat actions because as discussed before a comment is a thread so we can say export async function add comment to thread and it's going to accept a couple of props it's going to accept a thread ID which is going to be of a type string and we don't have that many so we can leave them in a regular function usually you can use an object as we defined before we can have a comment text which is going to be a string as well a user ID of a type string and a path of a type string immediately within it we of course want to connect to DB and then we can open up a try and catch Block in the catch we can say something like throw new error error adding commented thread where we can't lock the error.message but in the try we can focus on the logic of adding a comment now think about it how do we add a comment well first we have to find the original thread by its ID so we can say const original thread is equal to a weight thread dot find by ID and we pass in the thread ID coming through params if we don't have an original thread then we can simply throw a new error saying thread not found finally we have to create a new thread with the comment text so that's going to be const common thread is equal to new thread how cool is this we already have the model and the instance of the thread so it's going to be pretty simple to just create it and turn it into a comment so what does our thread have it has the text which is the common text it has the author which is equal to user ID and it has the parent ID which is the thread ID that is the ID of the original thread that we're commenting on now we want to save the Common Thread to the database save the new thread and we can do that by saying const saved comment thread is equal to a weight comment thread dot save then we want to update the original thread to include the new comment by saying original thread dot children dot push and want to push the saved Common Thread dot underscore ID and finally we want to save the original thread by saying a weight original thread dot save and then we want to revalidate the path so that it shows instantly pretty cool stuff right we already reused the majority of the functionality because we already have the thread logic done but now we're applying it to adding a comment so let's give it a shot let's call our add comment to thread and let's pass the thread ID common text user ID and the path that's going to happen in the common form where we can now await not create thread but add comment to thread which is coming from lib actions thread actions and now we're not going to pass this as an object we just have to pass the values one by one so first we can pass the thread ID which we already have access to then we have the values dot thread which is the actual value then we can Json God parse the current user ID as well as the path name and this path name is just the current URL and finally we do a form dot reset if you want to add yet another comment now let's give it a shot here we're talking about something cool about next 13 but let's say write a mad comment to that tweet saying next.js 13 is bad there we go sorry but I disagree next 13 might seem flashy but it's nothing but hype okay this is pretty cool so let's copy this and let's add it as a comment right here and click reply we immediately get an error it looks like next is not going to allow us to talk badly about it but let's see we have a throw new error error adding commented thread cannot read properties of undefined reading push okay this is interesting so let's go back to post thread and to thread actions and where do we have the push here original thread that children this right here was supposed to be children so if we fix this hopefully I will now be able to click reply one more time and the form reset happened which I'm guessing means that the comment went through so before we actually implement the logic of checking it out we can go back to our mongodb database and see if it's there back in Atlas we can go to our cluster zero collections threads and then we should have three threads here and we do sorry but I have to disagree and it seems like it went through two times because even the original one worked so no worries we can easily delete the second instance of it right here in the database so we have just one so now I'm guessing it's gonna have a parent ID and that's the original one have the children oh I cannot seem to see the child getting added right here it's possible because we deleted the wrong one so let's simply just try to give the same comment one more time and now if we go back and if we refresh go to threads we have three we can see that it has a parent ID and this one doesn't seem to have children but I just figure out I'm looking for the children the wrong one we should be looking at the one with the rocket emoji so and this one is here and this one still has two children even though we only have one that's because of the error that happened at the start but no worries we can delete this entire thread and just try it one more time just to be sure right we're gonna delete the comment as well there we go so now we have one we can go back to home page we can see one we can go to create thread and we can pose the same good one talking about next 13. and then we can comment on it and now if we go back and refresh you can see we have three this one has one child and it is the 405 405 it's looking good so now let's actually list out the comments below the thread that should look something like this so let's get started we can close all of the currently open files besides the page.dsx that is within the thread so now we go below by creating a new Dev that's going to have a class name equal to mt-10 for margin top and then we render the thread children so we can say thread dot children dot map where we get each individual child item of a type any is okay for now and then we can just return what well it's going to be believe it or not a thread card and we already have this thread hard here so we can copy everything and we can simply paste it over here but we'll have to modify all of this thread to child item so let's simply say child item here and let's see if we have to add any additional functionalities we have to say is comment which is a Boolean variable set to true to indicate that we can modify something within this thread card great so there we go another thread appeared indicating that this is a comment to the original one but as you can notice the layout should be a bit different right it should mean that this is the original one and these are comments added to it and here it looks like we just have two so let's fix that let's go into the thread card and let's modify the original article by making this class name dynamic everything is going to be fine but we can make a check we can see if is comment if that is the case we can add padding x0 on extra small devices padding X7 and usually we're going to add this BG dark 2 and p7 so that's only going to be here as the second part of this ternary operation there we go so now if we save this you can notice that it indicates that this is a comment and this is the original although it's a bit weird because we have the same logo appearing in all the places I also noticed that the image is different because this one is being picked up from Clerk and here we have the one from our database so if we go to our thread here we can pass in the user info and then Dot what does it have the image URL I believe let's check it out no it's not image URL let's check our model to know how to restore it by going to models user it is going to be just image so let's go back and say image right here this should get the correct one of our user I reloaded and it appeared so this is pretty cool now we are rendering the first comment that we have although this is a bit weird because I'm disagreeing with myself here but hopefully we're gonna fix that once more of you come onto our platform great now what do you think we should do next we can do search which is pretty cool we can also do activities we can do communities and we can do profile there's a lot of stuff that we are yet to add I believe profile is the next reasonable step because it's going to allow us to see all of our threads but also later on navigate to profiles of other users so in the route let's create a new profile folder and within it we're going to have the ID which is of course going to be within square brackets indicating it is dynamic just as our thread and we can render a page.tsx right within it our profile is going to be another page another asynchronous functional component we can return a section and we can say profile of course we can do export default page as well now if we save this and if we click somewhere where we can go to the profile I believe on this profile photo as well or just clicking here it's going to lead us to this empty page which means that now we are ready to start implementing the profile page as with the create thread we'll have to figure out if we currently have a user and if that user has onboarded because only then we can go to the profile page so let's navigate to create thread page and inside of here we'll be able to copy the Imports as well as these first few checks that we are making so let's go ahead and copy everything from here to here and we can paste it right here so we're importing the fetch user from user actions the current user and the redirect and this is going to be everything that we need so far but what are we going to do within our post page well here when we fetch the user info we won't be fetching the ID of the currently logged in user because maybe sometimes we're looking at the profile page of another user and that's exactly why we're passing this user ID in the navigation bar so right here in the profile we can destructure the params which next.js provides us and we can say that the params is going to contain an object with an ID of a type string and then instead of providing user.id which is the currently logged in user we can provide params.id whichever one we are currently checking out great now we're going to have a section and then we want to render a component called a profile header which is going to contain these sorts of things so there we can simply render a self-closing profile header component that's going to look something like this of course this is going to be a new component so we can go to components shared and then create a new profile header dot TSX there we can simply create a new const profile header is equal to a component or rather a functional component that's going to have a return where we're going to have a div and there we can render an H1 that's going to say profile header and of course we can do export default profile header now we have to import this back in here by double clicking pressing Ctrl space and then importing it from add forward slash components shared and then profile header and we'll need to pass some additional params or prompts to it so let's pass the account ID of the user we're looking at so that's going to be user info dot ID let's also pass the auth user ID this is going to allow us to figure out if the currently logged in user is the one looking at their own profile right now or if they're looking at somebody else's then we have the name which is userinfo.name we have the username which is userinfo.username we have the IMG URL which is userinfo.image and finally we have a bio which is userinfo.bio now we can dive into the profile and accept all of these props at the top that's going to look like this we can just destructure them account ID auth user ID name username imgurl bio and this is all that we have for now these can go in one line or multiple lines whatever you prefer I think they can nicely fit like this and then we have to say this is of a type props and then at the top we can define an interface props and now we can add account ID of a type string or the user ID of a type string name of a type string username of a type string image URL if we type string and bio of a type string and we have everything we need to start creating this profile card or the header so let's get started by creating the structure using a couple of divs and let's go back to our application that we're working on right now so let's give it a class name equal to flex W-4 Flex Dash call and justify Dash start within it let's create a new div that's going to have a class name equal to flex items Dash Center and justify Dash between and within it we can have another div that's going to have a class name equal to flex items Dash Center and gap-3 you might be wondering why do we have so many divs but this is just to create the desired structure we have to have a structure for the image and then we put the username and the name side by side and then the description or bio below finally we have one last div that's going to unwrap our image So within it let's create a new self-closing image tag that we can import from next image and we can give it a source equal to IMG URL an altag equal to profile image a fill property as well as a class name equal to rounded Dash full object dash cover and Shadow Dash 2XL now if we save this and go back and reload the page you can see a huge profile photo up here right here and that's why we usually wrap an image in a div when working in nextgs because we can give it a class name equal to relative h-20 w-20 and then object dash cover and this is now going to wrap our image and make it smaller and now we can continue with adding the other info by going below this div creating another one and this one is just going to have a class name equal to flex-1 and it's going to have an H2 that's going to render the name of course to be able to see this name we have to give it a class name equal to text Dash left text Dash heading 3 Dash bold and text dash light dash one and we can save it now we can see this name and the right below the name we want to have a P tag that's going to render the ad and then we can render the username of course we also have to give it a class name equal to text Dash base Dash Medium as well as text Dash gray dash one and we can save it there we go this is looking much better and then we just have to render the bio then we can go to devs down later on if we're looking at the community profile then here we'll be able to render to do community but for now we can just move over down to the bio and that's going to be a P tag that's going to render the bio and it's going to have a class name equal to margin top 6 Max Dash W Dash LG for Max width text Dash base Dash regular and text dash light Dash 2. and right below it we're going to have a self-closing div that's going to have a class name equal to margin top 12 H dash 0.5 so just 0.5 height W Dash full and then BG dark three if we save this this is now going to add the bio as well as this line which is barely visible but it definitely differentiates the header from the content below now of course this is not really looking good and that's because we have to go one more div outside with these elements so we can move this div at the end all the way here to close it before and then we have the P tag and the div outside of one of those divs and if we save it you can see it jumps down because it's no longer in the flex container great with that we can now start rendering the tabs which we can switch between and then see all of our different threads and I want to mention that all of this is going to be reusable we're going to reuse the profile page for communities and we're going to also reuse the tabs but for communities they're going to say something different like the second tab is going to say members pretty cool now let's move forward to the actual tabs by exiting our profile header and moving below it right below it we're going to render a Dev that's going to have a class name equal to margin top of nine and then we want to start rendering the tabs tabs are going to come from shot CN so you already know the drill we can simply say something like MPX chat CN UI add latest add and then we can say tabs this is going to immediately install them and allow us to use it there we go we can say yes and we are ready so now we can render the tabs right here and within the tabs we're going to render the tab list and we can import all of these components coming from chat CN so here we can say import tabs as well as tabs content as well as tabs list and tabs trigger and all of that is coming from add forward slash components forward slash UI forward slash tabs great and here we're supposed to have an S tabs list there we go so our tabs is going to have a default value equal to threads and it's going to have a class name equal to W-4 now if we save this you can see that we already start seeing some kind of a structure up here but of course we have to provide a data to our tabs so here we can have a class name equal to tab and that's just going to expand it a bit and make it look more like a tab and then we want to Loop over the tabs and those tabs are going to be saved within our constants so if you go to our constants index.js here you can see profile tabs and Community tabs so we'll be able to Simply Loop through them so open a new Dynamic block of code and say profile tabs dot map where we have each individual Tab and we want to instantly return something known as a tabs trigger for each one of these tabs like a button opening a tab within the tabs trigger we want to have a self-closing image which we of course have to import from next image as well below the image we want to have a P tag that's going to render the tab.label and that label can be hidden on small devices so we can say class name Max SM hidden and now we can start adding data to our tabs trigger first since we have profile tabs we have to import them from add forward slash constants then we have to provide a key equal to tab.label we also have to provide a value equal to tab that value and a class name equal to tab this image is going to have a source equal to tab that icon and an ALT tag equal to tab that label as well as the width is equal to 24 height is equal to 24 and the class name is going to be equal to architect contain now if we save this you can see that we have three tabs and we can switch through them this is looking pretty good now on smaller devices we won't be able to fit all of these which is exactly why we're hiding the label and just keeping the icon pretty cool stuff and you can see how powerful chat CN is of course here I told you that you have to provide a value a key wrap everything in a Taps Trigger or tabs list but of course by reading the documentation you would be able to figure this out on your own as well now with the threads one we also have to add this number saying how many threads does a user have so that's going to be right below the P tag where we can say if tap that label is triple equal to threads with a capital T then and only then render AP tag that's going to have a class name equal to margin left one to divide it from the left side rounded Dash SM BG Dash lite-4 padding X of 2 padding y of one not text Dash tiny Dash medium this is going to apply a specific font size and then text dash light Dash 2 and within it we can simply render what user info question mark dot threads dot length and if we save this we can see that we have created three different threads now of course if the number says three heck we want to see those three below so let's move outside of the tabs trigger as well as the tabs list and render the profile tabs that map where we have each individual Tab and for each one we want to render a tabs content this tabs content is going to have a key equal to content Dash and then we can render the tab.label to make it unique it's going to have a value equal to tab.value as well as a class name equal to wool and text dash light dash one within it we want to render a new component called threads tab and to it we can pass the current user ID equal to the user ID the account ID equal to the user info that ID and then the account type which is going to be equal to a string of user and then here we'll be able to figure out if we are the creator of this post or not if we are on our own profile looking at our own threads then there's going to be a delete button right here allowing us to delete the thread otherwise it won't be so we have to pass this additional info to figure that out and now let's go into the components and create a new shared component called thread or rather threads tab dot TSX and there we can create a new const threads tab is equal to an async functional component that is for now going to just return a section that can say threads tab and of course we have to do export default threads tab going back we can import it coming from components shared threads Tab and of course now tab script is complaining that we have to pass the right props to it so going back right here let's do just that we're going to accept the current user ID as well as the account ID and finally the account type and that's going to be of a type is props and you already know the drill props is going to be an interface so we can say interface props and we can say current user ID is a string a count ID is a string and account type is a string as well finally inside of here we'll be able to fetch specific posts that only belong to this specific user or a specific organization or community and then we can render all of them below so our next job is to create a function that's going to fetch profile threads and that is not that hard considering that we have our structure put in place so if we go to our actions user actions and go just below the fetch user we can create a new one export async function fetch user posts this one is going to require a user ID of a type string and immediately within it we can open up a new try and catch block and try to connect to the database once we do that we have to find all threads authored by the user with the given user ID so we can do that by saying cause threads is equal to a weight user dot find one where the ID is equal to user ID and then we have to call the dot populate method on it we want to populate the path of threads where the model is equal to thread which we have to import from above by dot slash models thread dot model and then we want to populate one more time where we have a path of children a model equal to thread and an even deeper populate where we have the path is equal to author so we want to know the author even of a child thread where it's going to be a model of user and we only want to select the name the image and the ID which we can pass in a string just like so later on we'll also have to populate the community as well so I can add a to-do populate community there we go just so we don't forget now we're populating it and we can simply return the threads if we have an error we can simply throw a new error failed to fetch posts and the error is of a type any and with that we're done with the fetch user post which means that we can return here and just call it straight away so we can say let result is equal to a weight fetch user posts and there we can pass in the account ID of course this fetch user post is coming from add forward slash lib for slash actions forward slash user dot actions and now if we don't have a result so if no result we can simply redirect to forward slash and this redirect is coming from next navigation so once we do get all the results what we can do is render a section with a class name equal to mt9 flex Flex Dash call and gap-10 this is the div or section inside of which we're going to render our posts so let's do just that let's say result dot threads dot map where we have each individual thread and for each one of these we want to return can you guess what well a thread card yes thankfully we made our life easier from the get-go by creating the thread card before so what we can do now is find where we're calling This Thread card we're already doing it in a couple of places and we can simply copy its instance so let's copy this thread card which is from the thread Details page and we can paste it right here we'll have to give it a key an ID a current user ID which for now can simply be current user ID a parent ID text later on we might need to update the author as well as the community to indicate whether we are the owners of the community and whether we are the authors or somebody else's but for now this will do so let's import the thread card and we can say that the thread is of a type any just for now and we can save this and if we fix this typo right here hopefully we'll be able to see our three posts there we go it's looking good but the profile photo is broken so as I said we'll have to update our author and we can do that by figuring out if the account type is triple equal to user which in this case it is because we're passing it if that is the case then we can have a ternary operator so we can say if account type is equal to user then we can return a name which is result.name this result is coming right from our database image is going to be result dot image and ID is going to be resolve.id else if this is not the account type user then we'll have to get the data directly from the thread itself so we can say else name is thread.author.name image is thread.author.image and ID is thread.author.id we're going to explain this in more detail later on now here I have an extra column so we can remove that and we should be good and there we go now we can see the name and the profile photo and if you look at the number of threads it's going to say three but we can only see two this is definitely something we can look into later and try to fix it but what does this mean well it means that we have successfully created a profile photo now let's go back to our home page and I was about to say let's try to visit somebody else's profile but we don't have any but not to worry we can easily log out and create a new account so let's do just that I'm going to click log out we can then navigate to sign in and as you can see we'll also have to Center this form but we can leave that for later now let's simply log in maybe this time using GitHub go through the onboarding I'm gonna give myself a username of a full JavaScript Mastery this time and a name of blue JSM because we're gonna switch the profile photo to the blue logo so we can differentiate the two profiles and I'm gonna say um blue right here and click submit this just redirected us to the home page but I hope you can see something weird happening right here even though these two posts were created by the original JSM account now it says that they were created by Blue JSM which is definitely not the case and if we go to our profile it simply breaks because it should go to profile forward slash ID but we can fix that later on for now let's figure out why these are saying blue JSM and not the regular one and to check that we can go to components cards and then the thread card and figure out where we're rendering the author that's going to be right here author name and also we have author image right here and this author is coming right here as a prop to the thread card so we have to figure out on our home page where we're calling the thread card how are we passing it so right here we can see that the current user ID and the author is simply set to author and that belongs to the author of the post which is coming from the results which seems to be good right we're fetching all the posts so why would the author be the one that didn't actually create it it should be the one that added it to the database let's figure that out if we quickly open up our database deployments and go to our cluster zero and then our collections and our users you're gonna see something quite weird there's only one user right there with three posts this means that both my Gmail and my GitHub use the same email account which is why my accounts were merged into one which is okay so now let's go back to our App log out one more time and then sign in with an account of a different email now this one can be the real JSM and I can say JavaScript Mastery because the other one is a blue one and let's do JS Mastery and say the real JSM and click submit and we can go ahead and fix this navigation link by going to our nav shared and then left sidebar and then here we have our links there we go is active and we just want to ensure that the profile link points the right route so here we can say if link dot route is triple equal to forward slash profile in that case we can modify the link.rout to be equal to link dot route but then forward slash user ID and this user ID where is it coming from well it's coming from use auth which is coming from clerk so right here at the top we can import right here use auth and then we can say const user ID is equal to use auth there we go and we can make that equal now if we save this and if we click profile it's going to go to profile and then user ID and now you can see it points to the correct one with zero threads now let's try to create a new thread and it usually takes some time to navigate to this page but that's okay on the deployed version it's going to be instantaneous let's say I'm posting from the original JSM account just to be sure and click post we got redirected we can reload the page and there we go now we can see our post now if we go to our own profile we can now see one thread and if we go to the blue JSM we can see the blue jsm1 this is great this makes sense and now our profiles are working of course we can try to comment on the blue jsm's posts and there we go they're disagreeing with themselves but here we can say something like no no I agree and let's click reply just to check the commenting functionality and there we go it works now on the finished version on a specific post there should be some space between the comments and here we don't have a lot of space so let's go to our thread Details page which is going to be in the root thread and then page and then here we are going over our thread cards and they are labeled as is comment so now if we go into our thread card we can use this as comment to add that margin on the top and that can happen right here we're rendering the content and then below we can give this class name we can make a dynamic first of all and then say if is comment in that case we can also give it a margin bottom of 10. this is going to apply that only to the bottom of the first comment in the second one so it's going to provide additional space and make it look great awesome so now we have the home page we can switch between different profiles we can also leave comments on other people's profiles already this is a pretty hefty social media application but as you know we're going to bring it a step further we are yet to create our profile search as well as our activities and then finally the communities so let's start by doing it step by step now that we have the profiles we can search for them so let's close all of the currently opened files we can collapse the file tree go to the app Root and create a new folder called search within it we can create a new page.tsx there we can do our usual stuff I can even use the rafce which is going to quickly spin up a page which is going to be called page and we don't need to import react it is going to be an asynchronous page and for now we can return a section that's simply going to have an H1 equal to class name is equal to head Dash text and margin bottom of 10 and it's going to say search now as soon as we save this we can now navigate to our search which does take some time but as I said later on nextgs is going to optimize it for the deployed version and we can see our search and while we're here why don't we just create all of the pages that we're going to have later on so we can copy this entire page and create a new folder called activity and within activity create a new page.tsx and we can simply paste it right here and switch this to activity that's going to allow us to be able to switch to the heart icon as well which is the activity page and we can also create communities so let's create a new folder called communities and create a new page.tsx paste it and say communities this is now going to allow us to switch to communities as well making all of our six icons or six different paths work flawlessly now this is pretty weird while I was creating communities it auto created a layout but we don't need it here so I can delete it and once I do it automatically gets created what is that it's possible because I created it right here not in the root but it should have been in the root so let's move it to the root folder and then remove the layout and hopefully that's going to make it work Yep this is perfect and now it suits our original root layout great so as I said let's start from the top to the bottom starting with the profile search and we can move from there that's going to be in the search page DSX and we can close the other files here we can do a similar thing we've done in the profile page which is just check if the user currently exists and if they have properly been on boarded so we can copy those four lines as well as the majority of the Imports and paste them right here on top of this page here we won't have params so for now we can clear this out and we won't be importing any tabs but we do need General stuff like getting the user and then fetching that user data to check whether they have been onboarded now with that said we'll have to create an additional function which is to fetch all the users so we can write that down fetch users and of course this is another action within R lib actions user actions so let's get started with creating the fetch users right here by saying export async function fetch users within it later on we're going to pass a lot of things but for now we can leave it empty and then finally we have a try and catch block where we can first try to connect to DB as we usually do now for this we're going to implement the entire pagination functionality similar to what we have done for our posts so here we have to first calculate the number of users to skip based on the page number and Page size so we can say const skip amount is equal to page number minus 1 times the page size then we need to create a case insensitive regular expression for when we're searching the users so we can say const regex is equal to new reg X where we pass in the search string and make it lowercase i which means that it is case insensitive now of course the page number this is going to be here the page size and all of these other things are going to be coming from our params so right here let's pass them in one by one we're going to have a user ID a search string which if it doesn't exist can default to an empty string a page number which can default to one a page size which can default to something like 20 and a sort by which can default to something like desk as in descending and there we go now we're passing all of these things which is great and of course we have to Define the types of things so we can Define them here by saying user ID is a string search string is optional and it is a string page number is optional and it is a number page size is optional and it is a number and then sort by is going to be a sort order which is coming from Mongoose so we can import it right here if you click it it's just going to be a type of minus one one ascending descending or similar so we always know what we are sorting by there we go so now we can proceed with our functionality of fetching and sorting first we can create an initial query to get the users that's going to be const query is equal to an object where we can simply have an ID and we can say dollar sign and e as in not equal to user ID so we want to filter out our current user then we need to check if a search string exists so we can say if search string dot trim is not triple equal to an empty string in that case we can proceed with the search and append a query by saying query dot dollar sign or is equal to an array where we search for a username that then looks into regex equal to regex we created before as well as it can search a name of a similar regular expression so we're searching both by name and by username because maybe somebody knows you by your name or your nickname so this or is saying that property or does not exist on type ID not equal to string and that is this query right here which means that we have to give this query a type so we can see this is of a type filter query which is coming from mongoose and we can further Define it because it's a filter query for the type of user and if we fix this now now it shouldn't complain anymore because it knows that this query is going to have access to the or object great now we are doing the fetching we are preparing for the skipping we're doing the searching but now we can also do sorting so we can Define the sort options by saying cons sort options is equal to and then we can say create it add and then we can sort it by sort by then we can also do const users query finally we can get them based on all of these search and filtering and querying criteria by saying user.find pass in the query and then we can do the dot sort with sort options we can do a DOT skip based on the skip amount and then that limit on the page size rate then we can get the total user count by saying cons total users count and we're going to use this to know the total number of pages for the users by saying a weight user dot count documents and we pass in the query as well and this sort by that you're seeing here should be sort by Not Sorry by so I'm glad I got that fixed now once we have the user's query we can finally execute it by saying const users is equal to a weight users query dot exec and now based on the total number of users we can know is there a next page by saying const is next is equal to Total users count is greater than skip amount plus users dot length similar to what we've done with the threads and the total user count should be total users count there we go and finally we can now return all of the users as well as the is next property if we have an error you know the drill we can simply throw a new error saying failed to fetch users and set the errors of a type any and with that we're done with the fetch users action and going back we can now call that action so here we can say const result is equal to a weight fetch users and we can import that from user actions at the top once we get it we have to pass all of the params to it we have to pass the user ID equal to user.id we have to pass a search string which for now we can leave as an empty string because we're going to implement that later on the page number for now can be simply one and finally the page size can be something like 25 and it looks like I imported a wrong function thank you typescript for telling me that I imported fetch user posts instead of the newly created fetch users function so let's import fetch users coming from add forward slash lib actions user actions great now if we save this we finally have the results and now we can render them right here to show all the different profiles that exist on our app for now it should be one because besides our currently logged in user there is another one so what we can do is we can render later on a search bar right here but we can leave that for later for now we just want to render all of them so we can have a div that's going to have a class name equal to margin top of 14 Flex Flex Dash coal and GAP Dash 9. inside of there we can say result.users dot length is triple equal to zero if that is the case then we simply say something like no users and we can also give it a class name say no result this is going to just apply a specific styling to that text if we do have the users though we can render a react fragment within which we're going to have a result that users.map where we go over each specific let's call it a person in this case and we're going to render a user card which is a special self-closing component so now we can go to our components specifically to cards and create a new user card dot TSX in there we can run rafce or simply create a basic user card and now going back we can import it from components cards user card great to that card we can pass a key equal to the person dot ID we can also pass an ID equal to the person.id let's pass a name equal to the person.name we also need to pass a username equal to person.username the image URL equal to person.image and we're going to also reuse this card later on for communities so now we can see person type is equal to just a string of user later on we're gonna make it work for communities now we have to accept all of these props in the user card so let's do just that we can say that we want to get the ID the name the username the image URL as well as the person type and that's going to be equal to a type or rather an interface of props which we can Define right here so that's going to be interface props where we have an ID of a type string name of a type string username of a type string image URL of a type string and a person type of a type string great and we can start rendering that person card trust me it's going to be a breeze we can wrap everything in an article the best way to wrap a typical card and give it a class name equal to user Dash card if we save this we should already be able to see just one card appear somewhere although it's not really that noticeable maybe once we add the profile image we'll be able to see it so let's create a div that's going to say class name is equal to user Dash card underscore Avatar and within it we can render a self-closing image tag that's going to have a source equal to IMG URL the alt tag is going to be logo the width is going to be 48 the height is going to be 48 as well and the class name is going to be rounded Dash full and of course we have to import image coming from next image once we do that hopefully we'll be able to see something on the search and there we go we can see the blue JSM logo let's also render its name and the username we can do that below this image still within the div by creating a new Dev with a class name equal to flex-1 text Dash ellipsis that's going to be a Double L right here and this is going to ensure that it applies the text overflow of Ellipsis saying that dot dot if the name is too long then we can render an H4 that's going to render a name and we can give it a class name equal to text Dash base Dash semi-bold as well as text dash light Dash 1. if we save it the name should be visible right below it we can render a P tag that's going to have a class name equal to text Dash small Dash medium as well as text Dash gray dash 1 and it's going to render at and then a dynamic username okay okay this is looking more like it and finally at the bottom we want to have a button that's going to say view so this is going to be below these two devs and this button is going to come from UI button and it's going to say View by default the button is not going to be anything special but of course we can style it by giving it a class name equal to user Dash card underscore BTN we can also give it an on click property that's going to be a callback function where we can simply use our router so we can create a new instance of the router at the top by importing it so import use router coming from next forward slash router we can create an instance of it by saying const router is equal to use router and then here we can use the router by saying router dot push and we want to push to forward slash profile forward slash and then the ID now if we save this you can see that now we have to turn this into a client component so here we can say use client and it's giving me an error that the next router was not mounted that's because I made a mistake the used router is coming from next navigation so it looks like I had the old next 12 ways of doing it but next navigation is the right way to do it now and now we can click View and it's going to lead us to the finished profile page of the blue JSM the finished application search looks something like this we have a card with all of these different users and here we have just one which is exactly how it should be because so far we have created one other account other than our own so for now it wouldn't even make a lot of sense to add the search functionality as of yet because we have no data to search for but we can come back to this later on for now we can move on to the next part which is implementing the activity page this is going to be quite interesting this is like a notifications tab allowing you to see when somebody else commented on your post so that's pretty useful not always do you build out notifications When developing these social media applications at least not a lot of people teach you but today I will so now we can move to the app at this point you should already be aware of our entire file and folder structure as you can see once you build it out it is scalable you just keep coming back and forth between components lib actions models and then all the way to the pages so let's go to root activity and then page and this is where we can get started with implementing it once again we're going to have those similar checks that we usually have so we can go to our search and we can simply copy these first initial few Imports and lines and then we can paste it right here so once again this just included the const user is equal to a weight current user and then it allows us to check whether the user has onboarded of course we won't be needing many of these Imports so we can remove them but the one we will need is the import to our new action which is going to be called get activity okay or get notifications if you prefer so now we can go back to the user actions that's going to be within lib actions user actions and right below fetch users we can Implement Our Last Action of the day at least the last user action which is export async function get activity and the only thing it needs is the user ID of a type string there we can try to connect to the DB if we succeed great if not no worries we can simply throw a new error that's going to look like this failed to fetch activity and then we can render the error Dot message now if we do manage to get it that's great we can first find all threads created by the user and we can do that by saying const user threads is equal to a weight thread.find where the author is the user ID once we find that we want to collect all the child thread IDs which are the actual replies or comments from the children field pretty cool stuff right we're going deeper and deeper and we want to get all of the comment IDs we can do that by saying const child thread IDs is equal to user threads that we just fetched dot reduce okay so we're using a native JavaScript reduce function we have the accumulator as well as the user thread which is the object We're looping over and then we want to return the accumulator and we want to concat user thread dot children I know this is a bit more complex but Hey whenever you have a code that looks like this let's ask Chad GPT to explain it this is a great tip that I can give you don't be afraid of AI just let it be your friend so I'm going to say explain this function and let's paste it right here okay let's see it utilizes array reduce to accumulate the child threat IDs from an array of user threads let's break it down step by step user threads is an array containing multiple objects each representing a user thread okay that's pretty simple now each thread object is expected to have a property called children yes which are the commons first we declare a new variable or want to store all comment IDs then we use the array reduce method on that user threads that takes a callback function as its first argument the Callback function will be executed once for each element in the user threads to accumulate the child thread IDs so think of it as a map or as a for each it's going to go over all of the elements all of the threads and it's going to dive into their children now the question is what does it want to do with those children well we're going to accumulate it so we're going to keep track of all the children elements that we passed to it and it's going to hold the current result and then it's going to always get the new user thread every new iteration finally once everything gets processed we're going to return meaning concat a new array by merging the elements of the accumulator with the element of the last user thread that children array so one more time the reduce method will iterate over each element in the user threads array and at each step it will concatenate the child threat IDs of the current user thread object to the accumulated result oof this was a tough one right but in summary this function takes an array of user threads which are just nothing more than threads that that user has posted right each having a children property which is an array of comments that holds an array of child thread IDs comments right it uses array reduce to concatenate all the child threat IDs from each user thread into a single array which is then stored in child thread IDs as a matter of fact let me ask Chachi PT if it can give me an example give me an example of the data and hopefully it will let's see there we go so we have user threads with the ID and children and then we go over it and then it's going to just output the children but now we have to make it make sense right the children are an array of comments make the children array of random tweet comments okay it didn't understand the example doesn't matter I'm gonna take it here and we're gonna make it make sense right so we have something like this and these are all the different strings of different commas that we have something saying like this is great the other one is going to say this is bad and this is going to say this is terrible this is going to say okay this is going to say bad and this is all that we need now we're going to go through each one of these take the current element right store it in a new array which is this one go to the next one take additional elements and add it to that array okay so we're essentially just collecting all the comments that is it I know it might be a bit complicated but take your time don't let your brain explode and let's go through this together the more practice you take the better you'll become finally we want to get access to all of the replies excluding the ones created by the same user so we can say const replies is equal to a weight thread dot find where the underscore ID of the thread is in child threat IDs and then the author is not the current user ID so we can exclude the threads created by the same user that is searching for this and then we can call the dot populate which we use many times so far with a path of author as well as a model of user and which Fields do we want to get well we can select the name image and the underscore ID just like so and this is going to give us back our replies which we can simply return just like so now once again I know this is a more complicated function but one more time let's give Chan GPT to explain it explain this in simple terms and let's see if it's going to do it is AI going to change the world or not okay this is way too complicated of an explanation explain it to me like I'm 5. okay let's see if this is going to work and let's also say shorter this function helps someone find all the comments they received on their post from others on a computer it gathers all the comments from different places puts them together and shows them to the person if there's a problem it tells the person something is wrong it's referring to this error here so this is pretty good gets all the comments shows you that you have new replies that's it now we can go back to the activity and we can actually call this by saying const activity is equal to a weight get activity and we can import it from lib actions user actions and we need to pass the user info dot underscore ID to it once we have it we can Loop over all of those comments how do we do that well we can create a new section that's going to have a class name equal to margin top of 10 Flex Flex Dash coal Gap Dash five and there we can say Activity dot length is greater than zero so if we actually do have any activity then render an empty react fragment inside of which we can say Activity dot map where we get each individual activity and we can display a new link pointing to that comment right there now of course if we don't have any comments we can simply return a P tag that's going to say no activity yet now this link is coming from next forward slash link it needs a key considering we're mapping over it so we can say activity that underscore ID is the key the href is actually going to point to the thread Details page so it can be four slash thread forward slash activity dot parent ID and inside of there we can render an article that's going to have a class name equal to activity Dash card and there we can render an image of the person that posted dot comment of course we have to import image coming from next forward slash image we have to give it a source equal to activity dot author dot image alt is going to be profile picture width is going to be about 20. height of about 20 as well and the class name as usual with profile photos rounded Dash full and object dash cover now if we save this we'll see if we have any photos as of yet it looks like we still have a problem with ac.comcat being not a function let's see and that's because I forgot to pass a default value to the concat function so we need to provide like in the use effect hook also a default value here this is not a dependency array like usual but rather this is the default accumulator that we have to add to the reduce function if we added the issue gets fixed and right here we can see no activity yet so let's go ahead and fix that by giving it a class name of no text based regular as well as text lite-3 if we do it we should be able to see no activity yet why is there no activity well that's because blue JSM didn't yet comment on any of our posts so what we can do is we can comment on his and then once we go into his activity we'll be able to see that so let's just add a comment here on Nexus 13 is great and let's say something like no I have to disagree and click a reply there we go it automatically appears and that allows us now to log out and log in with GitHub which is going to get me into the blue JSM and let's fix the centering of this model while we're here by going to auth layout.tsx and let's wrap the children within a div so that's going to be a Dev that's going to have a class name which is going to be equal to W Dash full Flex justify Dash Center items Dash Center and then Min Dash H dash screen and we can put the children within it so if we do this and save it you can see that it is nicely positioned in the center of the screen so now let's log in with Json blue in my case or something else in your case some different account on which you left the comments on there we go and let's go to activity which is here and you can see that we have two different cards although it's hard to see what they're about right now so let's add a notification to let the user know what is actually happening so below this image we can add a P tag saying that's going to have a span element within it and that's going to say Activity dot author dot name and then below that span we can say replied to your thread and here we can add an extra empty space now if we save this it's going to be hard to notice it because it's all dark but if we give this P tag a class name of not text Dash small Dash regular and text dash light dash one and the span a class name of margin right or mr1 in text Dash primary-500 This should look great as you can see we now see that JavaScript Master reply to your thread here as well so now if we click it it's actually going to bring us to the thread where we can see JavaScript mastery's comment this is great we can also go back and try the second one and there we go this is the original one the three left not that long ago so as you can see our app is starting to make so much more sense we can go to the home page to see all of the posts we can search for different users for example seeing JavaScript Mastery right here and we can look at their profile and see all of their threads as well as check out the activity and I know this is pretty slow so far switching between different pages but as I said in the finished website it's going to be instantaneous great so now the activity page is done as well and with that almost all of our pages are done besides the community so let me show you how the community is going to look like on the finished website we're gonna have a lot of different communities that admins can give you access to and once you're a member of a community you can read all of the community's threads which different users can post within that community and each post is going to get this tag saying that it was posted within the community and you'll also be able to see the members of a community so now we're diving into much harder stuff once again this is not something that you usually see in YouTube tutorials heck not even in courses so I hope you're excited this is going to be a big feature but I hope as you can see we already have the infrastructure for it built the Community page is actually going to be a reused profile page we already have the profile header as well as the tabs and then here we'll be reusing the actual thread cards but that's not going to make it any simpler there's still a lot of logic that we have to work through together to make this functionality happen so let's close all of the currently open files and let's go to pages and then communities page.tsx and let's get started but if you think about it this would be a wrong place to start with communities it would be as if we were starting with a home page when we didn't yet have a create thread page we first need to be able to create a community to then be able to view it right here in the communities dashboard so let me show you how we're going to do that and to make that happen we're going to utilize a really important concept and that is the concept of web hooks a web Hook is an event that's going to happen once something gets triggered and it's going to send the request to process something else at the same time it's going to make more sense once we actually implement it but here's the key point of why we have to implement web hooks we can create an organization right here through clerk by clicking create organization and it's immediately going to open up a great interface where we can simply upload a profile photo enter the organization name a slide URL and be good to go but the question is how is this data from clerk going to get into our application instantly how are we going to append additional posts once they are made as part of our organization to do that we have to have web hooks waiting and listening for the events from clerk so we can then make additional actions and modify our database accordingly so let's get started by creating those webhook listeners and our web Hook is going to be in form of an API route so right here within the app we can create a new API folder or rather we already have it but within it we can create a new folder called webhook and inside of here we can create a new folder called Clerk and then within clerk we can create a new route dot TS and we can get started with creating our web hook if you visit Clerk's docs is also going to talk a bit about web hooks it says that they allow you to receive event notification with clerk web hooks so if you need to keep your local store in sync with up-to-date user info or you want to send a welcome email you'll need to set up a web hook so clerk can tell you about events that happen on your instance for information on how to set up web hooks in your instance or how to architecture your application you can check out our sync data to your backend guide which is exactly what we are going to do for the majority of use cases data syncing is not needed at all and this guide can be skipped such as for all the things that we've used so far logging in getting the user info and so much more but this application is more advanced and we want to show you how you can create those communities and sync their data so it is a common setup for applications that involves a front-end and some sort of a back-end storage system since authentication and user management happen on clerkside the data will eventually need to reach the applications backend the recommended way to sync data between Clerk and your application is via webhooks in this guide you'll learn how to enable web Hooks and how to set up your backend so that it is updated every time an event happens on your clerk instance first we're going to go to our clerk dashboard and you can get there by going to dashboard.clerk.com and opening your project great we even saw that we now have live users which we can see right here then we can go to webhooks and later on we'll be able to add an endpoint which is this exact route we'll be working on right now this can be an engrock server but in this case it's going to be the deployed endpoint of our current application then you'll be able to filter which events you want to listen and then sync the data to your clerk database this is more or less it I don't want you to read through these docs because I'm going to show you how to do all of that today and these are only some of the events that clerk supports you can track when an organization is created deleted updated or when the invitations have been accepted created revoked and even additional details about memberships we're gonna get events in this format and they even support typescript so you know exactly what your webhook event looks like and this is also important we'll be verifying our requests we're going to use the Civics package to power the web hooks so you can even verify the web hooks to ensure that they are valid and this is needed because usually the app will be susceptible to a number of attacks so I found this great part of the clerks documentation where they show us exactly how to create a web hook clerk app router web Hooks and this is coming directly from James Perkins so what I've done is taken the raw format of this entire file and then modified it slightly to work for our specific events the link to this route.ts is going to be in the description down below within a GitHub gist file so simply open it copy it and then paste it right here we're gonna go through every single little detail together so soon enough everything is going to start to make sense so in here I even included a couple of resources here is the sync data to your backend URL from the clerk docs and then even a resource of why we need to verify web Hooks and here are the Imports for the community actions file which we don't yet have which is going to be incredibly similar to User it's just not going to be creating a user or deleting a user but rather creating a community and deleting a community so let's create this new community.actions file by going all the way down to lib actions and then create a new community Dot actions.ts and this is going to be another file that you'll be able to find within your GitHub just down below so you can simply copy and paste it right here as I said the majority of the functions are going to be similar we're making connections to our database and then finding and or updating or deleting specific instances but no worries while going through these different functions we're going to explain how each and every one of them works for now we can just collapse them so it's easier to see what we're working with and of course these require additional models we already have the model for the user and the thread but now we can create a model for the community and here you'll have to slightly fix the import by saying it's coming from dot slash models and then forward slash Community thread and user now how is our community schema going to look like well believe it or not it's going to be incredibly similar to our user so let's simply say community Dot model dot TS and let's copy everything we have in our user and paste it right here within the community we can rename this to community schema the ID is going to be the same the username is going to be the same name same image same bio same but this time we're going to have an additional property of created by and this is going to be a reference of a type Mongoose that schema.types that object ID to the user a community can also have the threads just like a user can and then instead of onboarded in communities we're going to have access to the members which is going to be an array of type is equal to Mongoose that schema that types that object a d with a reference to user so multiple users can belong as members of a specific community finally we can say const community is equal to more increased ad models that community or mongoose that model community and Community schema and we have a model for the community so the actions should no longer complain great now we can go back to the webhook file that we're explaining not that long ago we have the actions to add member to community create Community delete remove and update we have different event types events and then we have a post route here we have the heads for this vix verification of our web hooks here we're activating the web hook in our clerk dashboard and we need to add a next clerk webhook secret but we can only do this after we add the endpoint so we'll add this later on to our EnV for now we can simply add it there as a reminder that something needs to be there so let's put it here next to clerk at the top next clerk webhook secret and we're going to fill it in later on and now this is where the most important part happens here we have different if statements so we're saying if event type is organization.created then we can collapse that and we have listen organization membership so this is if a member invites and accepts creation then we have if a member is deleted if organization is updated and if an organization is deleted so this is five events in total and here's the thing what we do is we listen for this event from clerk so once we actually go here and do something then we get all of this information from my web hook and then here's the kicker we call our own action such as create community and pass in all the details which then does everything we are used to so far in this case connects to the database finds the user and then creates a new community and attaches it to it so hopefully this makes sense we've gone through this process many many times and I didn't want you to write all of this code by hand again but if you want to you're more than welcome to just figure out what all of these functions do and ensure if it makes sense if you have any questions regarding these Community functions just let me know in the comments down below or send me an email and we're going to gladly reply we'll try to make these videos as engaging and as educational as possible so I hope that this introduction of web hooks no matter how hard it can be still open eyes for you of how these more complex applications are being done there's different ways in which we have to pass the data and we're doing that right now by exposing our web hook but we're not done just yet we have to do just that we have to expose this route and add it as an endpoint right here within Clerk's dashboard and to be able to do that we have to deploy our application so let's go back to the app and just ensure that everything we have so far is working which it is and now we can deploy it we're not deploying it and saying that we're finished we're just deploying it to be able to expose this API route that will then be able to add as the endpoint so let me show you how do we do that first we have to create a new repo for our app and of course we can do that by going to github.com forward slash new there you'll be able to choose your repo name for example threads app we can make it public and we can click create Repository we can use the second terminal to deploy our application by running git add dot get commit Dash M first commit and then we have to say get Branch Dash M Main git remote ad origin right here and finally git push U origin main this is going to push all of the code from our main branch locally to our repository and there we go in just seconds it is here if it's here it means that verse cell could also see it and through versel we're going to deploy our current application so let's click Start deploying and let's continue with GitHub I'm going to switch to my organization account and I'm going to click import the threads app it should appear right here if it doesn't for you just search the name of the app that you chose for your repository and click import here we can go to environment variables and we can copy all of the variables we have and simply paste them here it's going to add all of them there and we can click deploy if we don't have a lot of errors this is now going to deploy our app to the internet so that everyone will be able to use it but of course don't share the link just yet we still have many modifications to make before it is fully complete now in this case we've got an error which is totally okay we've pushed a lot of code so it's normal that everything doesn't go right on the first try so let's try to read the error message and figure out what is breaking this one doesn't really give us a lot of information so let's proceed and then down here we see a fail to compile property ID does not exist on type empty object so this right here is a type error which means that it's coming from typescript that's not really a problem at this point for now we just want to ensure that the app is deployed so that we can add this web hook as an endpoint to clerk so what we can do is go to next.config.js and right at the top we can add a new typescript property which is an object that's going to have ignore build errors set to true and we can just push this to GitHub by saying git add dot git commit Dash M we can say ignore TS errors and then get push if I'm not mistaken this should automatically retry our deployment and we're gonna see if it's going to succeed so I'm going to go to JS Mastery Pro and here we have all of our deployments a lot of the repos here are not going to be known to you right off the bat because they're not YouTube projects as you can see cohort 5 cohort 3 and a lot of different teams are here creating phenomenal applications within the JSM masterclass experience there the members get first-hand experience building projects just like the one you're building today but in a real life setting with other teammates and with the mentors such as myself guiding you all the way through with that said we can see that our threads app now has been successfully deployed it is ready and we can check it out by clicking visit and there we go I hope now you trusts me when I told you that it's going to be super quick once it's deployed first we need to log in so let's go ahead and log in with Google I logged in in one of my accounts and there we go we can immediately switch from all of the pages and it just feels so quick this is phenomenal next.js truly is the framework of the future if you use it in the right way so with that said our app is live but where is our endpoint well it's going to be under app API webhook Clerk and then rao.ts so let's copy our URL go to the dashboard and Clerk and click add endpoint paste the current URL that you copied and then let's navigate it's going to be within API webhook Clerk and that is it you can even add a description and here in the filter events section we want to take the events we want to listen to so in this case we want to know when the organization was deleted or updated as well as when the organization invitation has been accepted created or revoked and when organization membership was created deleted or updated as well this is going to be more than enough for now and now you can click create this is now going to expose a signing secret which is right here so you can view it and then you can copy it we'll have to add it to two places the first place is our DOT env.local and the second place is going to be our deployed version of the application so let's go back to our cell to where we started which is right here going to settings environment variables and then we want to modify the next clerk webhook Secret by going to edit and then pasting the secret right here and clicking save and we also want to rerun our deployment just to ensure that the changes have taken effect so let's click redeploy and click redeploy here as well great so now our web Hook is connected and it's actually tracking these requests which means that once we fire an event by for example creating an organization right here our web hook should read it once it reads it it should tell our actions and our actions are then going to tell our database which is going to make our clerk side as well as their backend side completely synced that is great so let's go ahead and close the deployed version of the application for now just so we don't make a mistake of making changes here and then wondering why they're not being applied to your website in the browser we can close this as well but we will keep this open because here we can monitor the requests we're making so let's go to our localhost 3000 reload the page just to ensure that we are live and we're not so we have to rerun our application right here yeah these are some things that you're going to learn as you go the website is not always going to be as it seems it seemed online but I just wanted to make a check and ensure that it truly is online before we proceed that comes with experience right so as you build these applications and as you're listening to me going through these problems you're gonna learn how to become a better developer yourself and in these videos I can only go through a limited amount of stuff because who's going to watch a an eight hour long video but thankfully with our courses we can go into much more depth so if you haven't already definitely stay excited for the next 13 course that's coming soon with that said now we're live and we can go to create organization and we can give it a shot I went online a bit and found a great photo for a new organization it's a bird it's going to be a bird's lovers community so let's upload a new image and then drag and drop it here how seamless it is using Clerk and let's call it just that bird lovers and let's create the organization in here you can automatically enter email addresses of people that you want to invite so that's going to be maybe in this case the second account we used to create the organization so JavaScript mastery00 gmail.com and you can enter more if you want to and make it a member or an admin a member can create posts but cannot invite other people and an admin can also invite other people with a free plan of clerk you can invite up to three members per community and with other plans you can of course invite as much as you want so let's for now invite just this user and click send invitations invitations have been sent and we can finish this is pretty crazy that once I published this repo online or rather once I published this to GitHub some people already found the link and started adding new threads to it so for a lucky few that are following me on GitHub you'll be able to add a post while I'm recording this which is pretty cool so let's see this user for example has created a profile and has simply one pose that says bio pretty cool but more importantly on top right you can see bird lovers admin and you can even manage your organization by inviting other people and seeing the statuses of these people as well as change settings of an organization pretty cool stuff now what's going to happen if we go to create thread are we creating it as ourselves or are we creating it as the organization well let's give it a shot let's post a thread here we can say something like clerk is great and post on localhost we still have to reload the page and there we go it was still posted by bluejsm which means that it's not yet reading that we're a part of a community so what we can do is close all of the currently open files and go to app Root create thread and here navigate to the post thread component and within here we can read the organization so first we can import use organization coming from add clerk forward slash nextgs then we can use that organization as a hook by saying const organization is equal to use organization and then on submit right here we can check if there is no organization in that case we can simply do what we have been doing so far which is creating a thread but if we do have an organization so else we can do the same thing but now we can pass additional values instead of null Community ID is now going to be organization dot ID and everything else is going to stay the same and now that I look at this code it could have been simplified if we go back and just say if organization then we can return organization ID else we can return now silly me I should have noticed this right off the bat but with that said we can now create a new thread and hopefully now it's going to read that organization.id but then on our actual thread card how do we know if it is an organization did we Implement that logic to be able to see that yet or not well let's find out let's go to cards and let's go to the thread card here we're nowhere mentioning the organization so let's add the part that's going to say this it should say the time date as well as the name of the community so let's add it right here that's going to be below comments so right here we can exit this block of code and then go one two three divs down here later on we're going to add the functionality to delete a thread so we can say delete thread and alongside that we're going to also add this cool thing about showing the number of replies with their logos so we can also duplicate this and say show comment logos but for now we're more interested in the community aspect of things So Below all of this we can check if we're not currently working on a comment and if we are within a community then we want to show The Following thing that's going to be a link that's going to have an href going to forward slash communities forward slash community dot ID this doesn't exist yet but we're going to create that page soon and we can give it a class name equal to margin top of five Flex item stash Center but what matters more for now is to be able to see that this is indeed created by a community so inside of here we can create a class name equal to text Dash subtle Dash medium and text Dash gray dash one and first within here we want to format the date so we can call the format date string coming from add forward slash lib forward slash utils and we can pass in the created ad and then here we can render the community we can add a dash and then say community dot name and then community and we can also render the image of that community by rendering a self-closing image tag with a source equal to community dot image an ALT tag equal to community dot name a width of 14. as well as a height of 14 and a class name equal to margin left one rounded Dash full and object dash cover if we now save this you won't be able to see anything because none of these posts were parts of communities but if we try to create a new one right now for example am I a part of a community and click post we can reload the page and there still is nothing so we can simply console log the community aspect and see whether we have something there of course this community has to be passed into the thread card so right now it seems like we have it but just to be sure let's check where are we calling This Thread card and we're calling it I believe from the home page so that's going to be root and then page and here it is and we're passing the community saying pose that community so indeed it should be there going back to our thread card let's try to counterlock the community and let's try to make sure that it is a bit more visible by doing something like this community community if we save this and open up a terminal we should be able to see something right here because I believe this is server side rendered although nothing seems to be appearing the thread card itself yep it is server side rendered there is nothing else there so we should be able to see at least some kind of a console log in the browser if we look at it it's not going to be there but in here maybe I should make my terminal a bit bigger so that I can actually see it but I cannot seem to see it because it didn't wrap it in curly braces so this was actually being rendered on the screen instead of just showing there so now if we save this we can see that the community indeed is null for all of these different posts so when creating a thread we have to console log and figure out if we are a part of a community already so here we're checking for the organization and if the organization ID exists we're passing it as the organization ID so let's do another console log right here and check for organization ID so we can say org ID or as a matter of fact we can conserlock the entire organization just like so so we can see what is in there and now we'll have to submit another post so let's say test and post and it seems like we got some kind of a warning only plain objects in past decline components from server components which isn't really all that useful and this org ID is actually on the client side because it is a form element so it should appear right here and it seems like we do have an organization and the ID is indeed here okay that means that our currently logged in user is a part of the organization so if we check for that organization and pass an organization.id to create thread we are creating it but are we fetching it here we are not we're always setting the community as null we let this be intentionally because before we didn't have access to communities but now we can actually modify our create thread action to actually utilize the concept of communities by simply getting that Community ID and then passing it right here and with the introduction of community some other things have changed as well in the thread.actions.ds so in the description down below you'll be able to find the updated actions file for threads which you can simply copy and paste right here you're going to notice that it's going to keep everything as it was for the create thread but it's going to add this part right here to first of all find the community and then later on to also update the community model to know that it has a new thread within it and then for user actions I believe not a lot will be changed because this actions file is mostly relating to the threat itself and the possibility of creating threads but just to be sure in that same GitHub gist there's also going to be the updated user.actions.ts which you can simply copy and paste right here that way we're ensuring that we're all on the same page and that you can proceed with the development of our app now immediately we're going to have some errors with our Imports as before we can update this to come from dot dot slash models forward slash and then Community threat and user and we can do the same thing right here at the top it's going to come from data slash models and then user thread and community and then within here we're now properly passing the community which should be read by our thread card so let's create one final post now that our backend is working as well let's say Birds are great because now we truly are a part of the bird lovers Community if we reload the page you can see birds are great and we have this great bird lovers Community part but it's not at the bottom of the thread as it should be so there's some spacing issue happening with our thread card that we can fix right away I believe that we can remove this console log and we can move this right one div below that way it's going to appear below here as well this is great so now we can see that blue Json posted a thread at 7 pm my time but it is a part of this bird lovers community and I can notice that we're missing a space right here before the dash so we can add an additional space just like so there we go if we save it it's looking great now of course we want to go to that Community page and see if some other people have posted on there as well but for now we don't have a page for the community details so let's create it we can go to app Root communities and then create a new folder with square brackets of ID and within it a new page.tsx this page is going to be incredibly similar to our profile page so let's go to profile ID page DSX and let's copy this entire thing and paste it over here now we can make some modifications to it and make it work for our community as well so what are the changes that we have to make well let's explore them together let's look into the Imports first we are going to need the image from next image as well as the current user those are the things that we always use next instead of our profile tabs which are right here threads replies and tagged we are going to get our community tabs so we can bring this up and then say community tabs there we go we are going to reuse the profile header as well as the threads Tab and all of the components from chat CN but in this case we won't have to fetch the user or we won't have to redirect so we can remove those two inside of here we're just wondering about our currently logged in user and then we want to get all of the community details which we can do by saying cons Community details is equal to a weight fetch Community details and then we pass the params.id which is the ID of that community this function is coming from lib actions community actions and once we have the community details we simply want to pass them to the profile header because it can be the header of a community as well so we can simply pass all of them here instead of user info and most importantly we're going to change the type of the profile header to community so you can see the profile header is going to complain so let's get into it and let's modify it so it also accepts the type which is going to be sometimes there so there's a question mark and it can be of a type of string user or string community and now we're accepting that type right here later on we'll be able to change our profile header depending on that but for now let's go back to our page and let's worry about our profile tabs these are going to become community tabs of course the second one within the community tabs is going to be members really important part of every Community now within it we're gonna not gonna have userinfo.threads.length but rather Community details dot threads and then a bit below here we don't have to Loop through the content anymore we can just manually render three different pieces of tabs content which is going to look like this the first tabs content is going to be threads tab where we're going to pass in the community details dot underscore ID and we're going to say community this tab's content is going to have a value equal to threads which is going to be simply a string and we no longer need a key it's simply going to have a class name of w full text dash light dash one we can duplicate this two times and we want to modify the second one because the second one is going to contain our members and the third one is going to contain requests so for the members what we're going to do here is pretty cool we can remove the threads tab and then render a new section this section is going to have a class name equal to margin top of nine Flex Flex Dash coal and GAP Dash 10. within there we can render Community details question mark dot members and then we can then map over those members each individual member can be of a type any for now and we can return a user card because we have already created it so we can simply import it from components cards user card and to it we can pass a key equal to member dot ID we can pass the ID equal to member.id let's also pass a name equal to member.name username equal to member.username imgurl equal to member dot image and a person type which is going to be user so this right here is a user and not a community so now if we save this I don't think we're going to have any errors which means that now we can go to the community Details page which we can get to by clicking our bird lovers Community right here and once we click here we seem to be redirected back to the home page and we can see some new friendly faces here but why is that well if you go into the threads tab you can see that here we have a redirect if we don't have the result and why wouldn't we have a result well because right now we're fetching user posts and this is not a user we're now working with an organization so what we have to do is we have to add a check rather we have to say let result at the top is of a type any and then what we can do is say if account type is triple equal to community in that case we can do a result or rather say result is equal to a weight fetch Community post right here which we can import from community actions else we can use that old fetch user posts that we had before so we can say fetch user posts there we go and in both cases we pass the account ID but here the account type is community so it's gonna know to fetch Community posts so now if we go back and if we click bird lovers community and for some reason the app broken me which happens from time to time but we have to stop it from running and then re-run it one more time and then we can test it out and hopefully it will work and now we are on the Community page where we can see the threads as well as the members this is great now of course if you want to create a thread as yourself you can still easily do that the only thing you have to do is you have to switch from bird lovers to your personal workspace and then the thread is going to be created as just yourself now that we can visit a community profile page it would make sense to be able to view all communities within our communities tab on the left so let's go into it and let's show what communities are available of course we can close all of the currently open files and then only open the communities but not the ID page rather just the regular page where right now it simply says communities if you think about it this page is going to be really similar to our search page where we can search for different profiles within the threads application and oh my God how many of you have already created different profiles and I haven't even advertised this anywhere I just published the repo online so now we can create a similar thing for communities but with a bit of a different design it's going to look something like this on the finished side where we have different cards for different communities and of course the search bar is going to be there as well but I don't want you to Simply follow along with everything I'm doing at the end of this whole segment once we add a few more features you'll have to do a couple of things on your own so some pretty cool stuff is coming your way but for now let's focus on implementing this look right here and we can do that by copying what we right now have on our search page as they are similar so let's copy it and paste it within the communities now inside of here as you can see we have a lot of imports and this is what we can see but now we're gonna try to make it work with the communities so inside of here instead of fetching users we'll want to fetch our communities which is another action that we have and it's going to come from community actions we have to be careful whether this is accepting an object or if it's just accepting different params in a row here we can see that it's accepting an object and we don't have a user ID so we just have a search string so we can delete the user ID and here we can say fetch communities now immediately it won't be able to render the users which makes sense right so instead of resolve.users all of this is going to be switched to communities and here we can render an individual Community within the dot map Community there we go now in this case we won't be rendering the user card rather we'll be rendering a new card called a community card so let's go to our components cards and let's create a community card right here by saying Community card dot TSX and we've already created a lot of cards together in this video so this one is going to be in the GitHub just down below where you'll be able to Simply copy it and paste it here it's going to be incredibly similar to the profile one it's just going to render the community username name image and more and then we'll be able to call it right here by importing it from components cards Community card and then we can reference all of this as Community instead of person we can also pass the bio equal to community.bio as well as members is equal to community dot members and we don't need the person type now if we save this we are fetching the communities we are rendering them and indeed we can see our first community so as you can see the search page wasn't that different from our community's search page and now if you click view it's actually going to redirect us to the community View and there we go now let's also try to create another Community by creating a new clerk organization I'm going to upload an image and in this case it can be a community of dog lovers so we can say dog lovers and we can create an organization and we can invite a different account this time I'm going to invite JSM masterclass experience gmail.com and give them the admin permissions and send the invitation great now if we go to communities you can see that we have two communities right here which is looking great and hopefully I have received the invitation on my email and indeed I have we have the invitation from threads to join dog lovers organization so let's accept it this redirected us to the Local Host but now we can log out redirect to sign in and then I can try logging in with that new email that was invited and you can see we are in but now we unfortunately don't see that we have been invited to an organization that's a bit weird let's try to accept the invite one more time and there we go now that we were in we can accept that invite and we can see the dog lovers community and we can also go to our profile and now after reloading the page it redirected me to the onboarding I'm guessing this should have happened before but hey it's here now once I visited the profile page something like this should happen for you as well I'm gonna choose a profile photo and I'm gonna choose one of our old JSM Pro logos with a different color here I can say JSM masterclass and say this is my bio and I can click submit in this case we get an error saying that username that to lowercase is not a function so it looks like it's not picking up the username from here let's try to modify it slightly by removing the underscore and then clicking submit and it still doesn't want to proceed so let's get back to our onboarding and let's figure out if we have properly passed all the data and we're back at the part that we should have done at the start I completely forgot about this remember how I told you that we have to connect the data coming from clerk with the data coming from our own database and this was supposed to be it so now here you know how to fetch the user from the database right well we can say const user info is equal to a weight fetch user and we can pass the user ID then we can check if user info question mark Dot onboarded then we can redirect to the home page just like cell and of course this is supposed to say on board did and we can import the fetch user from user actions as well as redirect coming from next navigation and the user itself and we have to ensure if the user exists just to deal with the typescript errors or Warnings so we can say if no user return null and now we get this user info and we are pairing it to the user data which is now complete which we're then passing to the account profile we should have done this a long time ago but I completely forgot it but we got it now hopefully you didn't create any new users after this because then you would have experienced an error If you experience that error before just let me know down in the comments and I'll try to add it somewhere so that other people don't experience it as well with that said we can now try to submit this and we again get an error saying username dot to lowercase is not a function which makes me seem that the username is not being passed properly so we're checking if user info that username exists then pass that otherwise simply pass the user that username let's simply console log it so we're going to console log the user info question mark dot username as well as user question mark dot username and then let's see what is there this should reach us right here and if all of these are empty strings then we might not actually be able to see it so I'm going to divide it into two different console logs and I'm going to add a message before say something like user info as well as simply user and now if we save this once we visit the onboarding page we can reload and then we should get something here so yeah you can see that user info is going to be undefined but the user will be there and this is coming from clerk so here we're checking if user info question mark that username and we're passing it but what we should have done is in all of these cases we should have checked if user info exists if it does then pass the user info that username else pass the user that username and we can repeat this procedure for the name bio and the image too so we can say if user info exists then user info dot name buyer image else simply do the other thing so now if we save this and try one more time we can say JSM masterclass and here we can see Master Class and we can say this is my bio and third time's the charm if we click submit it's going to throw us an error because still the username tool lowercase is not a function so let's see if we are properly passing it in if we go to the user data which is coming as user to the account profile and then here oh the error could be here we're passing the update user and in the meantime I was messing with the way that we're passing the props so if we go into it you can notice that here before we were accepting them just as a list of different strings into it but I told you that trick to Simply put them within an object so for you this could potentially be working all along but I have to switch this back to what I told you to do and once that is corrected we can now for hopefully the fourth time enter this choose our name enter our bio and click submit and there we go we're back and if we go to our profile you can actually see all the data here it's great to see that PNG profiles work as well because now this one has no background wonderful almost all parts of her application are done I hope you guys would put some better threads right here instead of just some simple ones but hey once it's actually deployed all of you will be able to use the final version of the application now while we're on the topic of the final version of the application we are incredibly close but we're not there yet I mean we have all of the pages we have the community Details page where you can see the members you can Traverse to their profiles to see that as well you can look at the thread detail pages and even leave comments as Rajan already has here and all of this is pretty cool stuff but there are a couple of parts of this application missing do you know which ones are those well if you check the full final application you're gonna notice that the communities as well as the search have the search bars right so that's one part that you can add to further improve this application there's also this little comment thing which I would like you to implement on your own to try it out so try to fetch different comments that we have and show the number of replies below each one of these posts it's the little things right this was a big application and it's possible that not every single part of it for you is working perfectly which is totally okay because I'm here for you whenever something is not working or if you want to check out the finished code for new features that haven't been implemented yet the link to the completely finished projects GitHub repository is going to be down in the description there you'll be able to reference the final code and ensure that your code is working as it should so with that said the final part that is left for us to do right now is to show you how to deploy this application so that once you finalize it you'll be able to deploy it as well I mean as a matter of fact you can deploy it with me right now and then continue working on it for years to come because there's so much more stuff that we can add to this and that's the beauty of web development and the beauty of learning through project-based applications so let me show you how we can deploy this application right away by killing our second terminal stopping this one from running by pressing Ctrl C and then y clearing it and then running get add dot git commit Dash m implement all the features and then git push this is now directly going to be pushed to our GitHub repository and with that it's also automatically going to get pushed to versl so you can see that right now our project is building and in about 30 or 40 seconds we'll be able to see it live if you go under deployments we can see that we are now live so let's go ahead and go to project and click visit we can close all of the other tabs and we can expand this to see it fully and as you expand it you'll be able to see the app in its full Glory but not only that you will have also expanded a hidden task that I was keeping for you here all the time and that is the right sidebar that we can only see on full HD devices there you can fetch all the suggested users maybe even think of a specific algorithm to figure out who your users might be interested in as well as older communities it's nothing more than simply taking what we already have in the search profile search as well as the communities simply take those and put them here as well for easy access I want you to be able to build stuff yourself and not just forever be stuck in tutorial hell that's why especially on these harder videos I want you to fully dive into the final repository code figure it out heck even use additional clerk functionalities because that's what relearning is all about and let's not forget to take a look at our app on the final deployed mobile version as well which is just looking phenomenal especially with that bottom navigation so with that said amazing job for coming to the end of this phenomenal tutorial huge thanks to clerk for making it possible not only by sponsoring this video but by creating such a phenomenal piece of software that significantly simplified the authentication process while at the same time making it so much more robust and if you came to the end of this video building it out while watching me build this for many many hours you're gonna be the perfect candidate for our Master Class there might have been a couple of times while we were watching this and you felt stuck on a specific problem or you didn't fully understand what I was telling you or what I was explaining well I created the master class for that exact reason you can build applications together in a team while getting expert Mentor assistance so think about building projects even more complicated at threads together within teams but then having a mentor there that you can ask for help 24 7 anytime in the day and of course the apps are specifically crafted up to date and modern for you to learn as much as possible oh and just so I don't forget this is the last chance to get your next gs13 ebook and get prepared for the upcoming nexgs 13 Pro course that we're launching soon the links are down in the description with that said once again thank you for being a JavaScript master and coming to the end of this video and have a wonderful day foreign [Music]
Info
Channel: JavaScript Mastery
Views: 424,380
Rating: undefined out of 5
Keywords: javascript, javascript mastery, js mastery, master javascript, nextjs, next.js, nextjs 13, next.js 13, next 13, next 13 app, nextjs 13 app, next.js 13 react, next.js 13.4, next 13.4, next js twitter, nextjs threads, nexjs react, nextjs routing, nextjs group routes, nextjs auth, clerk auth, nextjs full stack app, nextjs mern, nextjs 13 routing
Id: O5cmLDVTgAs
Channel Id: undefined
Length: 350min 35sec (21035 seconds)
Published: Fri Aug 04 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.