
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.
Complete source code available till this point of lesson is available at
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
00:00:02 To start utilizing our server action for fetching tags, let's actually display them on this page.
00:00:08 To do that, we can head over to app, root, tags, and then page.dsx, and then get access to the search params from the URL by declaring them right here,
00:00:19 search params of a type route params.
00:00:23 This is simply given to us by Next.js, and then we can destructure them right here by saying const page, page size, query,
00:00:32 and filter, which is equal to await search params.
00:00:37 Just the way we do it in the newer versions of Next.js.
00:00:40 So now that we're fetching dynamic properties from the URL search params, let's simply utilize them right here instead of these default values.
00:00:48 Page will become the number of page or one if we don't fetch it for some reason.
00:00:55 And the reason why we have to convert this into a number before using it is because every value that you get from search params by default is a string.
00:01:03 So even if someone types one, it'll come out as a string of one and then we have to manually convert it.
00:01:10 Same thing goes for the page size.
00:01:13 We get a number of the page size, or by default, we can leave it to 10. Then we get the access to the query, whatever the user has typed,
00:01:22 and then finally the filter.
00:01:24 If both the key and the value of an object are the same, then we can just omit it and leave it like this.
00:01:30 Now, instead of console logging it, let's actually develop the UI to be able to visualize our tags.
00:01:37 I'll start with an empty ReactFragment to wrap all of it together, and within it, I will display an h1.
00:01:44 That h1 will say tags, and it'll have a class name of h1-bold, text-dark100, light900, and text-3xl.
00:01:57 That'll give us a simple tags heading.
00:01:59 Next, let me create a new section that'll have some breathing room from the top heading by giving it a class name of margin top of 11. Within it,
00:02:10 we can render our local search bar, which is the component we have created so far.
00:02:14 So that'll be local search coming from components local search.
00:02:19 Let's go into it to just remind ourselves of what we need to pass into it.
00:02:24 It's going to be the route, the image source, the placeholder, and some other classes.
00:02:30 So first, let's pass the route, which is the most important part.
00:02:33 So on which route are we currently on and where are we trying to apply this search query?
00:02:39 It's going to be under routes.tags coming from constants routes.
00:02:43 Much better than manually spelling something like tags and then maybe forgetting the forward slash or just misspelling it like this.
00:02:50 After that, we can provide the image source for this search and that'll be forward slash icons forward slash search dot SVG.
00:02:59 After that, we can provide the placeholder, which will be equal to search by tag name, dot dot dot.
00:03:07 We can then provide the icon position, which will be equal to left and some other classes, which will be equal to flex dash one.
00:03:17 Now before, we didn't really have to change the icon position, so we haven't yet implemented that part into the local search bar.
00:03:24 So let's do it.
00:03:25 It is super simple.
00:03:27 We just add another prop of icon position, which will be either left or right, and then we can destructure it from the props,
00:03:36 icon position, and then we can use it right here at the top of our render.
00:03:41 or we have this image, we can wrap that image in curly braces and say if icon position is triple equal to left, then we can render this image and close it.
00:03:54 Else we can copy this, put it after the input and say if icon position is equal to right, then we render an image.
00:04:03 This one will be a bit smaller as it's on the end, so it's 15 15. And that's it.
00:04:08 So now we have made this component even more reusable.
00:04:11 And let's just fix the naming right here.
00:04:13 Icon Position.
00:04:15 Now let's see where we're calling this local search.
00:04:18 I think we called it once before, which is right here under app root on the homepage.
00:04:25 Now, since we changed the declaration of this function, you'll see that it'll complain that it's missing the icon position.
00:04:31 So let's also give it an icon position equal to left.
00:04:36 Or an alternative, if you think that in most cases you'll want the icon position of left, what you could do is just make it default to left,
00:04:46 like this, and then make this icon position prop optional.
00:04:50 That way, even if you don't provide this icon position, it'll be okay, and it'll use the default left one.
00:04:57 So you know what?
00:04:58 I will actually do that.
00:04:59 I will remove it from here.
00:05:02 Say I can position either left or right optional with the default being left.
00:05:07 And now we can also go to the tags and I can remove from here because by default it is left.
00:05:12 And back in the browser, we have a very nice local search bar, very similar to what we had on the home.
00:05:18 But here we were searching for questions and here we're searching for tags.
00:05:23 So we can actually say something like search tags.
00:05:27 Now, another super cool thing is that on the homepage, we were rendering a list of questions, and within tags, we'll render a list of tags.
00:05:37 And thankfully, we have already created our data renderer component.
00:05:42 It almost feels like we're creating our own UI library by how deep we went into making everything reusable and well-structured.
00:05:50 That's the level of depth that I want to teach you in this course.
00:05:54 So one day, if you want to develop your own UI library, or dive into some complex open source databases, you can do so very easily.
00:06:03 Now, what does our data renderer need?
00:06:05 It is super simple to know considering we have done a great job specifying the types right here.
00:06:11 So it needs success, data, empty, and render, as we can see right here.
00:06:15 So on success, we'll simply pass the success coming from the success of when we fetch the tags.
00:06:23 and we'll do the same thing with the error.
00:06:25 We get the error, we pass it to the renderer, and it'll do the rest.
00:06:29 Remember how it works?
00:06:30 If something fails, we have a complete state skeleton where we display either an error or some other message.
00:06:37 After that, we have some data to render.
00:06:39 In this case, data will actually be tags.
00:06:42 On the homepage, the data was questions.
00:06:45 Next, we have to choose how the empty state would look like.
00:06:48 So we can say on empty, show empty tags coming from constants states.
00:06:55 that'll simply look like this, no tags found, the tag cloud is empty.
00:07:00 Add some keywords to make it rain.
00:07:02 And finally, the most important part of the data renderer is the render function, where we can get access to the tags we're passing and then choose how
00:07:11 each one of these tags will be displayed.
00:07:13 For now, we can return a div for each one of the tags with a class name of margin top of 10, flex, w of full, flex-wrap,
00:07:24 and a gap of 4, just to make sure that it is fully mobile responsive and they stretch nicely.
00:07:30 Or, if they cannot stretch, they go into a new line.
00:07:33 Within it, we can map over the tags by saying tags.map, where we get each individual tag.
00:07:39 And for each tag, we can now return a tag card, coming from components, cards, card tag.
00:07:46 Thankfully, this is another component we have created before.
00:07:50 So it'll be super simple to just pass the key to it, which will be equal to tag.underscoreID, and spread the rest of the tag information.
00:08:00 That'll look something like this.
00:08:02 Now, if you go back to the tags page, you would expect to see three different tags, but instead, there is nothing.
00:08:09 Now, why is that?
00:08:10 Well, if you go into the tag card, you can notice that we have a prop called Compact that allows us to choose how this tag card will look like.
00:08:19 And currently, we're only ever returning something, like a button or a link, if we are rendering a compact version of the card,
00:08:27 because we haven't yet coded out the non-compact version.
00:08:31 So for the time being, to just quickly be able to check all the tags, let's say compact.
00:08:37 And then if you go back, you can see Node.js, JavaScript, and TypeScript.
00:08:41 This looks the same as on each one of the questions on the homepage, right here.
00:08:46 But that's not what we're after, right?
00:08:49 Because this doesn't look good on its own.
00:08:51 We need cards that are much larger, that have a nice proper title, an image, description, and even show the number of questions associated with that tag.
00:09:00 So first, we need a description.
00:09:03 Before, I asked ChatGPT to generate a file that'll provide an image for each one of these tags.
00:09:09 So if we head over to Lib, You can see this get dev icon class name.
00:09:16 And then here we have the tech map, where we're mapping over all of the most common technologies that our users could create tags about.
00:09:24 Now, alongside the tech map, or here, we have get dev icon class name.
00:09:29 we can create a function that'll give us a description for each one of these.
00:09:32 For now, I'll only do it for the most popular ones.
00:09:35 I'll copy the function that we have right now, and you can actually bear with me while I'm typing this out in front of you for the first time,
00:09:42 so you can see how I would approach prompting chadgbt.
00:09:46 Sometimes it can be hit or miss, but let's see what we get.
00:09:49 I'll say, I have a function that looks like this.
00:09:54 And I'll paste this function, which gets the name of a technology and normalizes it so that I can then attach an icon to each one of the technologies.
00:10:09 And I would actually want to provide an example of how that looks like.
00:10:12 So I have the tech map and I'll provide an example.
00:10:16 Okay, perfect.
00:10:17 Now, I need a similar function called getTechDescription, which accepts the tech name, normalizes it, and based on the object called techDescriptionMap,
00:10:38 which contains key value pairs of some of most popular technologies in the following format.
00:10:48 Let's do something like JavaScript, and then we can provide some kind of an example description, which is not too long, but not too short either.
00:10:57 You can just grab it from Google.
00:10:59 JavaScript is a powerful language for building dynamic, interactive, and modern web applications.
00:11:04 And I can provide another one, which is going to be for TypeScript.
00:11:07 Then I can say, extend that map with descriptions of other tech.
00:11:14 like React, Next.js, Node.js, Python, Java, C++, Git, Docker, MongoDB, MySQL.
00:11:26 I mean, you name it, right?
00:11:28 I think it'll get the idea.
00:11:29 Once you have enough, you can then say, and it returns the description.
00:11:35 or a wildcard that looks like this.
00:11:39 TechName is a technology or a tool widely used in web development, providing valuable features and capabilities.
00:11:46 Something generic, right?
00:11:47 But we still get a nice description.
00:11:50 So let's see what it actually comes up with.
00:11:52 This is my first try.
00:11:55 I'll run it.
00:11:56 In this case, I'm using O1, but you can use any kind of other model.
00:11:59 It should be fine.
00:12:01 And you can see that here's a quick solution.
00:12:04 First extend your tech description map, then create a get tech description function, just like you did with get dev icon class name.
00:12:11 Perfect.
00:12:12 So let's go ahead and copy this code and let's paste it right here on top of the utils.
00:12:17 So, we have some kind of tech descriptions.
00:12:20 It looks like it did a pretty good job filling them out.
00:12:23 And then we get getTechDescription, which normalizes the name and returns the description of that specific technology or just a generic value,
00:12:32 if it cannot find anything else.
00:12:34 Of course, you can keep adding more values to this tech description map.
00:12:38 If you notice that your users are adding some more tags that are getting a lot of traction and a lot of questions, you can go ahead and just manually add
00:12:44 it right here.
00:12:45 Or you could actually build some kind of an AI feature, which automatically fetches the information about that tech and then adds it right here.
00:12:53 That's fine too.
00:12:54 With that in mind, we can now go back to the page.
00:12:57 We're passing all the information about the tag.
00:12:59 The most important thing of course is its name because based off of the name we'll get the icon as well as the description.
00:13:06 And no longer we want to render a compact version.
00:13:10 Now we want to go into the tag card and develop a non-compact version which will show the tag in its full glory.
00:13:17 First things first, alongside the icon class I'll also get the icon description by saying const icon description.
00:13:24 is equal to get tech description coming from utils and I'll pass the name of that tag then I'll hit below the part where we say if compact and now we're
00:13:35 just gonna add a regular return so if it's not compact then we want to return a link This link will have an href pointing to routes.tag and to it we can
00:13:49 pass the ID of that tag.
00:13:50 So that'll be the underscore ID.
00:13:52 So it'll go to the details page of that tag.
00:13:54 We can also give it a class name equal to shadow-light100, dark none.
00:14:02 And within it, we can render an article.
00:14:04 Article is an HTML5 semantic component, which means that it covers all the information about that specific thing, like one tag will be one article.
00:14:14 We can give it a class name equal to background-light900, dark 200, with a light border, a flex container, w-full, flex-call,
00:14:30 rounded-2xl, border, padding x of 8 to give it some space, padding y of 10, and on small devices, W of 260 pixels.
00:14:43 That will create three spots within which we can place the data about each one of these tags.
00:14:49 Within the article, we can create a div with a class name equal to flex, items-center, justify-between, and a gap of three in between the elements.
00:15:01 Within that div, we can render another div with a class name of background-light800, underscore dark 400, with a W fit, rounded-sm,
00:15:17 padding X of 5, and padding Y of 1.5. And within it, we can render a P tag that'll render the name of the tag.
00:15:27 Let's make it a bit bolder by giving it a class name of paragraph-semi-bold and text-dark300 and light900.
00:15:37 There we go.
00:15:38 That's better.
00:15:38 Node.js, JavaScript, TypeScript.
00:15:41 We can head below this div that wraps the P tag and render an icon.
00:15:46 I, which is going to be self-closing, with a class name that accepts the CN for class names of icon class.
00:15:56 And then we can also give it a text to Excel as well as area hidden property of true.
00:16:04 Now we get a nice looking icon as well.
00:16:06 Let's head below this div and render a p tag.
00:16:10 This is the moment of truth where we're trying to render the description.
00:16:13 So let's give it a class name of small dash regular text dash dark 500 underscore light 700 margin top of five.
00:16:24 line-clampof3 and wfull.
00:16:28 The line-clamp will ensure that it doesn't go past three lines of content.
00:16:33 And here we can render the icon description.
00:16:37 There we go.
00:16:38 That's looking nice.
00:16:39 JavaScript is a powerful language for building modern dynamic interactive applications.
00:16:43 Node.js enables server-side JavaScript and TypeScript adds strong typing to JavaScript.
00:16:49 This is looking great.
00:16:50 Now let's also render the number of questions associated with each one of the tags by rendering a p tag with a class name equal to small dash medium.
00:17:03 text-dark 400, light 500, margin top of 3.5. And within it, we can render a span that'll have a class name equal to body-semi-bold.
00:17:17 primary-text-gradient and a margin right of 2.5, within which we can render the number of questions and then put a plus sign saying that there's more than
00:17:29 that number of questions.
00:17:30 And outside of this pan, we can say questions.
00:17:33 So this is number, like five, and then we save questions.
00:17:36 And now we can see currently we have one plus questions for each one of these.
00:17:40 Of course, as more come, it'll actually increment that number for real this time and not like right here on the right sidebar.
00:17:48 And with that in mind, we have now successfully developed a page where we can see all of the tags as well as search for some of these tags right here.
00:17:56 And you can see this proper no tags found model, but if I type something like node or JavaScript, it works perfectly well.
00:18:04 And we have reused a ton of things from the All Questions homepage.
00:18:09 such as the data renderer, the local search bar, even the logic from the server action that's fetching the actual data.
00:18:16 So great job on displaying the tags we have previously fetched.
00:18:21 But now, this page on its own is not very useful.
00:18:24 It simply tells us how many questions are associated with each tag, but not what those questions are.
00:18:29 So if we wanted to help some people answer some of the questions or get some help, we cannot really do that.
00:18:35 So what happens if we click on it?
00:18:37 we go to the Tags detail page.
00:18:39 And on this page, we can fetch all of the questions associated with each one of these tags and then display them similarly to how we display all the questions
00:18:49 on the homepage.
00:18:50 But here, we would only be displaying the questions related to that tag.
00:18:54 So with that in mind, let's go ahead and commit this.
00:18:58 I can simply say something like display tags, commit and sync.
00:19:03 And as always, great job and let's move on.