
No Comments Yet
Be the first to share your thoughts and start the conversation.
Be the first to share your thoughts and start the conversation.
👉 Bunny (create your free account and get one extra free month): https://jsm.dev/jsm-bunny
How did you manage to remove the blur property and reach here?
Upgrading gives you access to quizzes so you can test your knowledge, track progress, and improve your skills.
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
In this lesson, we explore the initial steps to create a reusable homepage interface in a React application, focusing on building shared components such as a navigation bar and header. The aim is to set up a structured and efficient layout that can be reused across different pages.
00:00:02 To get started working on our homepage, we have to consider which elements here are shared.
00:00:08 Like, what are some of the components that we can extract and use them across other pages so we can create them first?
00:00:15 Well, first things first, you can see that we have a very simple navigation bar with a favicon and a title at the left, and then a profile photo at the right.
00:00:26 And then there's also this so-called header component that says that we're currently viewing the public library of all videos.
00:00:34 We have a search, the most viewed filters as well, and then we have two call-to-action buttons to upload a video or directly record one.
00:00:43 And you can see that this header is then shared with the profile header as well.
00:00:48 The UI is the same, it's just the data that is different.
00:00:52 So let's focus on implementing the navigation bar first and then the header second.
00:00:57 We'll start right within the app folder, but not by creating a reusable component first, rather by thinking of how we want to approach the routing of our application.
00:01:08 See, if I head back over here and zoom out, you'll notice that all of these other pages have the nav bar at the top, right?
00:01:15 It is always present.
00:01:17 The only place that doesn't have it is the Auth page, which means that we want to create separate layouts for the Auth and then another separate layout
00:01:26 for all the other pages.
00:01:28 So to do that, I'll create a new folder within the app folder, and I'll call it within parentheses.
00:01:35 This is important because we're creating a route group.
00:01:38 Now, why is it a route group?
00:01:40 Because we don't want it to actually be included within the URL.
00:01:43 So forward slash root forward slash home.
00:01:46 Now, we just want to create it in order to be able to add the layout within it.
00:01:51 So then I'll create a new layout.tsx.
00:01:55 I'll run refce right within it.
00:01:58 And then I will move the current page right into the root.
00:02:02 And I'll create another directory called auth.
00:02:07 This is also a route group.
00:02:09 And within this auth, we're going to have a page.tsx.
00:02:14 where I'll also create rafce, just a regular auth page component.
00:02:21 Perfect.
00:02:22 So now, let's start with focusing on the layout of our homepages.
00:02:26 Basically, what we have to do is accept the children right here at the top, and children will be of a type react.reactNode,
00:02:36 because we're returning another page, or you can simply say reactNode and import that over from React.
00:02:44 Now, what do we want to return from this page?
00:02:46 Well, it'll be simple.
00:02:48 We just want to return a div.
00:02:50 That renders the children, but then above whatever page it is that we're rendering, we also want to show a navbar right here.
00:02:59 And this is our first reusable component of the day.
00:03:02 So I'll create a new components folder right outside of the app folder.
00:03:09 called components, and within the components folder, I'll create a new file called navbar.tsx.
00:03:18 And I'll run RAFCE and just import it over within our layout, just as a simple self-closing component that'll look something like this.
00:03:32 Perfect.
00:03:32 So now, if I do this, you'll notice that AppRoot cannot have two parallel pages that resolve to the same path.
00:03:40 And why is this happening?
00:03:42 Because route groups technically disappear as far as the routing is concerned, so we can see the page here and page here,
00:03:49 and they're both competing to become the main homepage.
00:03:53 So within the Auth folder, I'll actually create a new route called sign-in and then put the page within it.
00:04:01 So then this will become the sign-in and this will remain the home page.
00:04:05 So if I do this, you can now see that the navbar got added at the top of the Welcome to Loom clone.
00:04:11 So let's start it working on it first.
00:04:13 At this point, it might actually make sense to split my display into two.
00:04:17 On the left side, we can see the code.
00:04:19 And on the right side, we can see the changes of that code.
00:04:21 Of course, I'll zoom it out a bit so we can better see what's happening.
00:04:25 Perfect.
00:04:26 So heading over to our navbar, we can start implementing it.
00:04:30 Let's start by turning it into a semantic header tag and giving it a class name equal to navbar.
00:04:40 This will give it some breathing room.
00:04:42 Within it, we can create a semantic nav element and within it, render a link coming from next link with an href pointing to just forward slash,
00:04:53 meaning the homepage.
00:04:54 Within it, we can render an image also coming from next image with a source of forward slash assets, forward slash icons,
00:05:03 forward slash logo.svg.
00:05:06 So for the first time, we're using an additional asset that I gave you.
00:05:10 And for the alt tag, I'll just say logo.
00:05:14 We'll also give it a width of about 32 and a height of 32 pixels.
00:05:20 And then close it right here.
00:05:22 If you do this, you'll see this little logo up here on the top left.
00:05:27 And then we can also give it an H1 that's going to say snapcast.
00:05:32 Snapcast is the name of our full stack recording and sharing platform.
00:05:37 Of course, feel free to name it however you'd like.
00:05:40 Now we can head over below the link and here we can check whether a user exists or not.
00:05:46 User will of course be a session object that we'll be getting from our browser.
00:05:51 So for now I'll just define it as a static variable at the top.
00:05:54 const user is equal to an empty object for now.
00:05:59 So below I'll say if user exists, so user and If that is the case, we want to render a figure.
00:06:07 We typically use a figure when we want to put two things together, maybe like an image and a button.
00:06:12 And within that figure, I'll render a button.
00:06:15 And within the button, an image.
00:06:18 This image will later on have a source pointing to the real user's profile photo.
00:06:22 But for now, we can just render a dummy one.
00:06:25 I think I have it stored under forward slash assets, forward slash images, forward slash dummy dot jpeg.
00:06:33 And we can close it right there.
00:06:35 For the alt tag, we can say user.
00:06:37 We can give it a width of about 36 and the height of 36 as well.
00:06:43 And you should be able to see just an empty profile photo for now.
00:06:47 Let's not forget to give it a class name of rounded dash full and aspect dash square.
00:06:54 So now we have a rounded profile avatar.
00:06:57 Good enough for now, right?
00:06:59 Below that button, we can also render another button.
00:07:03 And the purpose of this button will be the logout functionality.
00:07:07 So I'll give it a class name equal to cursor.
00:07:13 dash pointer so people know that it is clickable.
00:07:16 And within it, I'll render another image with a source of assets, icons, logout.svg with an alt tag of logout, a width of about 24 and a height of 24 as
00:07:31 well with a class name of rotate 180. which will just rotate this door out, pointing to the user is going outside of the application.
00:07:41 And there you have it.
00:07:42 This is a very simple navigation bar.
00:07:45 Looks okay on desktop, has a bit of a padding on the left and right, so it nicely centers the content, and of course, looks good on mobile too.
00:07:54 So now we can exit that navigation bar and the layout too, and we can focus on the homepage.
00:08:01 Within the homepage, we want to replace this div with a main tag, and give it a class name of WrapperPage.
00:08:09 These two classes, if you look into them, will apply a max width to the entire window, ensuring that the content never stretches above 1440 pixels.
00:08:21 Why do we do this?
00:08:22 Because if you had a lot of text, you would not be able to read it all the way from the left to the right, or right to left,
00:08:29 right?
00:08:29 Rather, we somehow put it in the center so it's easier to skim through.
00:08:33 And the page class name will simply apply a flex column with a mid-age screen and some paddings so that elements nicely fall from top to bottom.
00:08:42 And now we want to create that reusable header component I was telling you about.
00:08:46 It's this one right here.
00:08:50 On the homepage, it says public library and all videos and two buttons.
00:08:54 And right here on the profile page, it rather shows the user information and then those two additional buttons.
00:09:01 So let's create it so we can reuse it across both of these pages.
00:09:05 Back into the code, within the components folder, I'll create a new file called header.tsx.
00:09:13 I will run RAFCE and I will simply import it right here within this page, right above the current h1 header.
00:09:23 And there it is.
00:09:25 Now, to make a component truly reusable, you have to ask yourself, what are the differences between this component being used in the homepage as opposed
00:09:34 to this component being used on the profile page?
00:09:36 So what kind of different information does it need?
00:09:39 In this case, it's going to be the header, the subheader, and if it's on profile, the user image.
00:09:46 So we can immediately make it accept those props.
00:09:49 That's going to be the subheader.
00:09:51 the title and the user image.
00:09:54 So user img and these will be of a type shared header props.
00:10:02 Now where are these share header props coming?
00:10:05 If you control or command click into it, you'll notice that it'll lead you to an index.d.ds file where it says that subheader and title are strings and
00:10:16 the user image is also a string, but it is optional because on the homepage, maybe we don't have it.
00:10:21 So this way, our TypeScript will know what each one of these properties is.
00:10:26 And now that we know what we have to pass, we can head back over to the homepage and immediately pass the right values.
00:10:32 So in this case, as the title, I will pass exactly what it says on the design, all videos.
00:10:39 And as the subheader, I will pass another string equal to public library.
00:10:45 Perfect.
00:10:46 And now if I head back into the header, we can accept those and render them within the page.
00:10:52 So let's start by rendering a semantic header tag.
00:10:57 with a class name equal to header.
00:10:59 Then right within it, I want to create a new section that'll have a class name equal to header dash container.
00:11:09 This will allow us to play a bit with the layout.
00:11:12 Within it, I'll create a new div with a class name equal to details.
00:11:18 And within the details, I want to check whether the user image exists.
00:11:24 So if it does, then I will render an image with a source.
00:11:30 of user image, and if it doesn't exist, I will provide it that fake dummy image.
00:11:36 So that's assets, rather forward slash assets, forward slash images, forward slash dummy.jpg.
00:11:45 For the alt tag, I'll provide the user.
00:11:49 For the width, I'll say about 66. Height can be 66 as well.
00:11:55 And the class name will be rounded-full.
00:11:59 And here I can close this image.
00:12:01 And if it actually existed, you would have been able to see it right here.
00:12:05 But right now we're in the homepage, so this part doesn't exist.
00:12:09 Rather, below this dynamic block of code, still within the div, I will render an article that will render a p tag of subheader and below it,
00:12:19 I'll render an h1 that'll render the actual title of the page.
00:12:24 So now we can see public library, all videos.
00:12:27 We can exit this div.
00:12:29 and create an aside.
00:12:31 So this will be on the side of the text.
00:12:33 Here, we can render a link coming from next link with an href pointing to forward slash upload.
00:12:41 And within the link, I will render another image with a source of assets, icons, upload.
00:12:49 And then I'll tag of upload with a width of about 16, a height of 16, 2. And we can close it right there.
00:13:00 And next to it, render a span element that says upload.
00:13:03 a video so if you save this you'll see an upload a video button appear right here and below it for now we can render another button with a class name equal
00:13:15 to primary-btn that'll render an image And this image will come from icons, so we can import them from constants, dot record,
00:13:26 with an alt tag of record, a width of 16, a height of 16 as well, and we can close it right there.
00:13:35 and below we can render a span that'll say record a video.
00:13:40 This one we also have to put within a div that'll have a class of its own, and this class will be record.
00:13:48 So if I do this and put this button right within it, This will give it some extra breathing room and apply the primary BTN styling.
00:13:56 Later on, we'll convert this into its own page, inside of which we'll add the functionality to actually live record a video.
00:14:04 And now we want to exit that aside as well as exit the section and create another section for the filters right below.
00:14:12 So it'll have a class name equal to search dash filter.
00:14:17 And within it, we can have a div that'll have a class name equal to search.
00:14:24 And within it, an input with a type is equal to text.
00:14:29 If we do this, you'll start seeing a new input appear right here.
00:14:34 I'll also give it a placeholder of search for videos.
00:14:39 tags and folders so we can search for anything and later on we'll also give it a value and an on change but for now i'll just leave it to be a dummy input
00:14:50 we can also render an image right here this is going to go below the input with a source of assets icons search.svg with an alt tag of search,
00:15:03 a width of about 16, and a height of about 16 as well.
00:15:08 So if I save it, you can see a little search icon appear right here.
00:15:12 And finally, later on, right here below this div, we'll also implement a drop-down.
00:15:18 So for now, I'll just make it say drop-down list, and later on, we'll turn this into an actual component.
00:15:25 For now, it can just be a piece of text that says drop-down list, just so we don't forget that we actually have to create this drop-down list component.
00:15:34 But yeah, if you check it out, this actually looks great on mobile, but it looks even better on desktop.
00:15:40 We already have the navbar and below the navbar, we have the dynamic header.
00:15:44 So while we're here, what do you say that we immediately add the profile route so I can show you how we can reuse this header component?
00:15:52 For example, when we click on this profile icon at the top right.
00:15:56 To do that, I'll head over to our homepage, or rather to our nav bar, and I will add a link to this user button.
00:16:05 So when we click on this button that has a dummy profile photo right now, I will use the router functionality.
00:16:13 So right at the top, I will say const router is equal to use router.
00:16:18 So we're utilizing a next navigation hook right here, not next router.
00:16:23 Make sure to import it from the right place.
00:16:25 And now it allows us to say router.push and we can push to forward slash profile forward slash some kind of an ID.
00:16:35 For now, I will leave it as a dummy ID, maybe like 123456. And as soon as we start using the use router within a page, we have to turn that page to be
00:16:46 client rendered.
00:16:47 Navbar is a small component, so I don't really mind if it's rendered on the client.
00:16:52 Perfect.
00:16:53 So now if you click on this profile photo, it'll lead us to a 404. So we better create it.
00:17:00 Right within the root right here, we can create another folder, which I will call profile.
00:17:07 And then within profile, we have to create another folder, which I'll call, within square brackets, ID.
00:17:15 Why within square brackets?
00:17:17 Because this is a dynamic route.
00:17:19 It's not always going to be forward slash one, two, three, four, five, six.
00:17:22 Sometimes it's going to be a different ID.
00:17:24 So this catches it and exposes it right on this profile page.
00:17:29 So within this ID, let's create a new page.tsx and run RAFCE.
00:17:36 If you create it, you'll immediately see that now we have this new page.
00:17:40 And how would you get access to this ID, specific ID of the user that we went to?
00:17:45 Well, you can get it through params.
00:17:47 So right at the top, you can just destructure the params and define that as params with search.
00:17:57 So now, if you wanted to display that ID of the user, you could very simply destructure it at the top by saying const id,
00:18:06 is equal to await params.
00:18:08 This is just how Next.js works.
00:18:11 It exposes this ID that you went to right here at the top.
00:18:17 through an async page, so you have to make it asynchronous to be able to weight the params.
00:18:22 And then you'll get access to it within this variable.
00:18:25 So I'll say user ID and then render it dynamically right here.
00:18:31 And we might as well make this a class name of text-to-excel and font-carla, just so it matches our application.
00:18:41 And there we go, you can see that now we're on this specific user's page.
00:18:44 And if what I've done right here still seems a bit confusing, it seems like you're not there yet to be able to watch this video when it comes to your Next.js skills.
00:18:53 So in that case, it might be best to pause right here and check out the ultimate Next.js course.
00:18:58 Here we go in a lot of detail on how everything works, starting from web basics and then moving over to app router.
00:19:07 and more importantly for you, the routing, right?
00:19:10 File-based routing, advanced routing, and in general, how to create dynamic routes and understand how to manage the state through the URL parameters in Next.js,
00:19:21 which is exactly what we'll be doing here.
00:19:23 I'll leave the link to this course down below.
00:19:25 So now that we have this additional page, we can also display a header right here.
00:19:31 So right below this text where we have the user ID, I will render a header component with a subheader of, well, we can maybe use some kind of an imaginary
00:19:42 text right now.
00:19:43 Like for the subheader, we can render the email, maybe adrian at jsmastery.pro.
00:19:50 For the title, we can render my name, Adrian JS Mastery.
00:19:55 And then maybe we can also provide a user image.
00:19:58 So I'll say user image is equal to forward slash assets, forward slash dummy, forward slash, well, what was it?
00:20:06 I think I forgot.
00:20:07 Oh yeah.
00:20:07 It's images dummy.
00:20:10 And then .jpg.
00:20:12 Perfect.
00:20:13 So now we're passing all of these important things that a header needs, and you can immediately see that it appears right here on the top.
00:20:19 So if we head over to the header, I don't think we need to provide this fallback option right here, because if we're rendering this image,
00:20:26 that means that we already have it, right?
00:20:28 And of course, to give it some breathing room, I'll actually move this class name over below to an H1 that will wrap this user ID.
00:20:38 And instead, I'll give this div a class name of wrapper page.
00:20:43 So now it looks a bit nicer.
00:20:45 And we can nicely switch between the homepage and the user page, and both of them share the same header.
00:20:52 Pretty cool, right?
00:20:53 And I think on desktop, it makes even more sense.
00:20:55 If you click right here, you can see that now we're on the profile page.
00:20:59 And if you click back here, we're on the homepage.
00:21:02 And both of these share the same header, but what matters most are the cards right below it.
00:21:08 And through this search and the drop-down list, we can filter through them.
00:21:12 Just so we can finalize this reusable header now that we're using it on both pages, let's actually develop this drop-down list UI as well.
00:21:21 So I'll head back over into header.
00:21:23 And right here, where we're rendering the drop-down list, I'll actually create a new drop-down component.
00:21:31 So let's head over into the components folder and create a new drop-down list.tsx.
00:21:38 You know the drill.
00:21:39 Run RAFCE.
00:21:41 and then just import it over where it needs to be used.
00:21:46 In this case, that is right here within the header.
00:21:49 And then head over into it so we can implement its UI.
00:21:51 In this case, I'll wrap it with a div with a class name of relative, as we'll need to make sure that the pop-up actually shows up in the right place.
00:22:00 Next, within it, I'll display another div with a class name equal to cursor-pointer.
00:22:07 And I'll give this element an onClick.
00:22:11 equal to and here we actually want to open up the drop down so I'll create a new use state snippet right here called is open and set is open at the start
00:22:26 set to false For this to work, we have to import the useState from React.
00:22:33 And since we're using a hook, we actually have to turn this into a useClient component, which once again, I don't mind because this is a very small component
00:22:42 that we're rendering.
00:22:43 Next, once we click on this button, we can just toggle the openState on or off.
00:22:48 So I'll say setIsOpen is not.
00:22:51 is open.
00:22:52 Perfect.
00:22:53 So we'll soon be able to toggle it on or off.
00:22:56 And within here, I will render another div with a class name equal to filter trigger within which I will render a figure that will contain the image pointing
00:23:10 to forward slash assets, forward slash icons, forward slash hamburger dot SVG with an alt tag of hamburger menu or just menu and a width of about 14, a
00:23:25 height of about 14 as well.
00:23:27 And we can close it right here.
00:23:28 So now you can see this menu, which means that we can click on it.
00:23:32 But then, we actually have to render something within it, such as the currently selected filter.
00:23:38 So for now, maybe I can just say most recent.
00:23:41 Later on, we'll implement real filters that we can filter by.
00:23:45 And then, below this figure, I will also render an image that'll have a source of assets.
00:23:52 icons arrow down.svg with an alt tag saying arrow down with a width of about 20, height of about 20, and we can close it right here.
00:24:06 So now this seems like an actual toggleable button.
00:24:09 Now, once we actually change the isOpen state, we can head over below this clickable div, this is the cursor pointer, and here we can check if the state
00:24:20 is open.
00:24:21 In that case, we can render a UL, an unordered list, with a class name equal to drop-down.
00:24:30 And here, we can map over some of the options that we want to render.
00:24:35 For now, I will simply render an array of different options.
00:24:40 It can be something like most recent, most liked, and so on.
00:24:45 And then we want to map over those options, getting each individual option, and for each one, automatically returning an LI.
00:24:53 Now, what do I mean when I say automatically return?
00:24:56 I mean to put parentheses right here instead of curly braces, because if you put a curly brace, it'll open up a new functional block,
00:25:04 but with this, it's an immediate return.
00:25:06 So we can return an li with a key equal to this option, because each option is unique, and it can actually render what that option says.
00:25:15 So now you can see most recent, most liked, if you actually clicked on it, right?
00:25:19 There we go.
00:25:20 And we can also style it a bit.
00:25:22 So let's give it a class name equal to ListItem, which it'll always have.
00:25:29 But then later on, we'll have to change the UI for the selected elements.
00:25:34 But for now, this is more than okay.
00:25:35 We have the hover effects, and it's not yet functional.
00:25:38 So now we can exit out of this drop-down list.
00:25:41 Later on, of course, we'll have to pass additional props to it to make it functional.
00:25:45 But for now, if you look into it, it actually looks and feels good.
00:25:50 We have the search UI, we have the filters UI, we have different buttons that allow us to move over to upload a video or to automatically record it,
00:25:59 and we can switch over between the profile page and the homepage.
00:26:04 But of course, the next main part that differentiates these pages are the video cards.
00:26:10 So on the homepage, we want to display other people's video cards and on the profile page, we want to display our video cards.
00:26:18 So in the next lesson, let's develop the reusable video card and display a bunch of them right here.