
Join the Conversation!
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
"Please login to view comments"
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
Complete source code for this 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
##Looks like we found a thief monkey By the way, I liked the trick how you reached till here. You have a good sense of humor. You will improve a lot if you join our course with this passion.
var
(function-scoped, outdated)let
(block-scoped, modern and recommended)const
(block-scoped, cannot be reassigned)_
, or $
let let = 5;
is invalid)myVar
and myvar
are different)string
, number
, boolean
, null
, undefined
, bigint
, symbol
Objects
, Arrays
, Functions
Subscribing gives you access to a brief, insightful summary of each lecture to stay on track.
00:00:02 Now that the most of the question details page has been completed, we are almost done with the question details page.
00:00:09 We can see the name of the author that posted the question, the question title, additional information, and the question itself with a nicely rendered
00:00:18 code block.
00:00:18 We have also implemented the number of views, and in the future, we'll add votes as well.
00:00:24 But for the time being, there's something much more important that we have to do, and that is to implement the answers functionality.
00:00:31 Yep, what would the question be without its answer?
00:00:35 So here, we can create a new section that will allow us to render all of the answers that we have so far, and we can sort them based on the number of upvotes
00:00:43 or popularity.
00:00:44 But even before that, we have to create a create answer form through which we can submit solutions if we know the answer to the asked question.
00:00:53 So with that in mind, back in the code, let's go over to components, forms, And let's create a new form and call it answerform.tsx.
00:01:06 Run RAFCE to very quickly spin up a simple form.
00:01:10 And then let's head over to our question details page.
00:01:13 So that is the question ID.
00:01:15 And let's scroll down to the bottom of that page.
00:01:18 Here, we're mapping over the tags, but now we want to create a new section right below that div.
00:01:24 This section will have a class name equal to margin Y of five divided a bit from the top and bottom.
00:01:31 And there we can render this answer form coming from components answer form like this.
00:01:37 There we go.
00:01:38 We can immediately see it on the screen.
00:01:39 Now this form will need some kind of validation.
00:01:42 And this one will be super simple.
00:01:44 The only thing we need is the content of the answer.
00:01:47 So let's head over to validations.ts and at the bottom, let's export const a new answer schema, which is going to be equal to z.object and it'll have one
00:02:01 property of content, which will be of a type z.string and we'll leave it as minimum of 100 characters.
00:02:10 And we can say answer has to have more than 100 characters.
00:02:16 because we want it to be actually a meaningful answer, and not just shitposts like on Reddit or current Stack Overflow.
00:02:23 Here, hopefully, developers will be nice enough to write a meaningful answer.
00:02:27 Finally, let's implement that answer form by heading back into the component.
00:02:32 And you already know the deal with forms, right?
00:02:34 We can start by copying everything from ShadCN, by creating a new form schema, validation, implementing the used form from React Hook form,
00:02:44 adding the onSubmit, and so on.
00:02:46 But instead of copying it from here, what do you say that we copy it from one of our simpler forms that we already have,
00:02:52 such as the odd form?
00:02:54 Let's copy everything all the way from here.
00:02:57 The start of the form component and all the way into the top so we get the imports as well.
00:03:04 And then we can simply remove this return and the top and paste it there.
00:03:09 Of course, you'll have to properly close this form as well as the major form that we have here.
00:03:15 After that, you'll have to properly close the return and export default, the answer form.
00:03:21 So make sure to rename it here at the top from odd form to answer form.
00:03:25 If you save it, we can then deal with all of the other imports, which we might or might not need.
00:03:31 We will need the Zod resolver.
00:03:33 No need for the link right here.
00:03:34 No need for the router, I think, for now.
00:03:37 From React hook form, we'll just use the form to keep it simple for now.
00:03:40 And from Zod, we'll just use Z.
00:03:43 We can import the button from components button.
00:03:45 And we can get the form along with the form field, form control, form item.
00:03:50 And I just don't think we need the label in this case.
00:03:53 For now, I'll also get rid of the input.
00:03:55 And we don't need the routes or toasts.
00:03:57 We can get those later on.
00:03:59 And let's also not forget to import the answer schema coming from lib validations.
00:04:05 Okay, perfect.
00:04:06 In this case, this form doesn't need to accept any props as it's going to be much simpler than before.
00:04:11 So let's just make it a simple functional component.
00:04:14 We don't need to import the router and we don't have to define any special types right here at the top.
00:04:20 But we do need a couple of use states.
00:04:22 So I'll create a new use state snippet and I'll call it is submitting and set is submitting at the start set to false as well as use state for the AI submitting.
00:04:36 So is AI.
00:04:39 Submitting.
00:04:40 And setIsAISubmitting at the start set to false.
00:04:44 This is going to be a very cool functionality where we'll actually integrate with OpenAI to allow it to modify our answer and make it much more understandable
00:04:53 for the person that's asking the question.
00:04:55 Let's also import useState from React.
00:04:58 After that, we have these default values.
00:05:01 And instead of saying schema right here, it'll be a type of answer schema.
00:05:05 And we actually get that answer schema right here.
00:05:09 And for the default values, it can be just an empty object that'll have a content set to an empty string like this, because that's the only field we have.
00:05:20 After that, we have the handle submit, which will basically become the handle create answer.
00:05:24 We don't need to specify any types right here.
00:05:27 It's just an asynchronous function that takes in some values.
00:05:30 And those values will be of a type z.infer.
00:05:34 type of answer schema basically it'll just have the content and for the time being we can just console log that we don't have to have any of this logic
00:05:43 right here so just console log the values to see if we're getting them no need to have this button Okay, so if I save this,
00:05:52 go back to localhost 3000 and reload.
00:05:54 We don't really see anything because we have an empty form.
00:05:58 But if I give this form a class name, something like margin top of 6, flex, w-full, flex-column and a gap of 10, and we create our first form field inside
00:06:12 of it, which will look something like this.
00:06:15 form field, it has to have a control, which will be equal to form that control, a name equal to content, and a render function to let it know what it'll render.
00:06:28 Here, we can destructure the field's properties, and we can return a form item that'll have a class name equal to flex w-full flex-call and a gap of three.
00:06:44 Within it, we'll have a form control that'll have a class name equal to margin top of 3.5. And within it, we can render our editor.
00:06:56 This is the same markdown editor that we had before coming from dot dot slash editor.
00:07:02 And remember when we used it before, there was something else that we had to do.
00:07:06 We have to ensure that it only loads on client side and not get pre-rendered on the server side.
00:07:11 I believe we have done this when we used it for the question form as well.
00:07:14 So if we go to the question form, you can see what we did.
00:07:17 We imported it and then set SSR false.
00:07:21 So let's copy this part from the question form.
00:07:23 And instead of manually importing it, let's say that it'll be a dynamic import coming from NextDynamic.
00:07:30 And then we get that editor.
00:07:31 Of course, to that editor, we have to provide some props, such as a value equal to field.value, the editorRef equal to editorRef,
00:07:41 which we of course have to create at the top.
00:07:45 So let's just define it right here by saying const editor ref is equal to use ref hook coming from react of a type MDX editor methods and at the start
00:08:00 equal to null.
00:08:01 And we now have this great markdown editor that allows us to provide an answer to this question.
00:08:06 Let's give it another prop.
00:08:09 called field change equal to field dot on change.
00:08:16 Perfect.
00:08:16 Just below this form field, let's also add a div with a class name equal to flex justify dash end.
00:08:26 And within it, we can render a button.
00:08:30 This button will be of a type is equal to submit with a class name of primary dash gradient.
00:08:38 and a w-fit.
00:08:41 Depending on the submitting status, we can render a different message.
00:08:44 So if isSubmitting, then we can render a new empty ReactFragment.
00:08:52 And within it, we can render a new reload icon coming from Radix UI icons with a class name of marginRight of 2, size of 4, and animate-spin.
00:09:07 So while the answer is being submitted, we'll be able to see it.
00:09:10 And we can also say something like posting dot dot dot.
00:09:14 And here we can say something like post answer.
00:09:19 Perfect.
00:09:20 So now we have post answer in case we're not submitting.
00:09:23 Also later on, we'll add the ability to create an AI answer.
00:09:28 So to do that, let's actually wrap this entire form in a div right here at the top.
00:09:34 I'll create a new div and wrap the entire form with it.
00:09:38 After that, right above the form, we can create another div, which will serve as the container for the AI button.
00:09:45 And within it, we can also have an H4.
00:09:48 Write your answer here, so like a title.
00:09:52 And below the H4, we can have a button with a class name equal to BTN and a disabled property.
00:10:00 If isAISubmitting is true, then we'll disable it because it's already submitting.
00:10:06 And then we can do something similar like what we have done right here for the submitting status.
00:10:10 So let's copy this isSubmitting status.
00:10:13 Based it here, but this time it'll be, is AI submitting?
00:10:19 If it is, we'll show a reload icon and we'll say generating because AI is generating and else we'll render an empty react fragment that'll have an image
00:10:31 within it.
00:10:32 This image will have a source of icons stars dot SVG with an alt tag of generate AI answer, a width of something like 12 and a height of 12 as well with
00:10:48 a class name of object contain.
00:10:51 And below this image, we can also say generate AI answer.
00:10:59 Of course, don't forget to import the image from next image.
00:11:02 And now we have this writer answer here, generate AI image, but let's further style this H4 and the button.
00:11:10 Let's start with this div that's wrapping the two by giving it a class name of flex, flex-call, justify-between.
00:11:22 Let's style the H4 by giving it a class name of paragraph-semi-bold.
00:11:39 Text dash dark 400, light 800. And let's style the button by giving it a class name of BTN, light dash border dash two, gap of 1.5, rounded dash MD,
00:11:55 border, padding X of four, padding Y of 2.5, text dash primary dash 500, shadow dash none, and in dark mode text dash primary 500. And now this looks like
00:12:11 a proper generate AI answer with these sparkle stars, which I believe has become the icon or the visualization part of what it means to generate using AI.
00:12:23 Of course, we'll implement this functionality later.
00:12:25 For now, we just want to make sure that we can actually submit anything written by ourselves.
00:12:31 So if I type something and click post answer, nothing will really happen, at least not yet.
00:12:37 But for now, we know that this is looking good on mobile devices.
00:12:41 It's looking amazing on desktop devices as well.
00:12:45 And if we open up the console, Type more than 100 characters and then post the answer.
00:12:53 You can see the content submitted right here.
00:12:56 If it's less than 100 characters, you won't be able to submit it.
00:12:59 So you won't be able to see any console logs, but we at least know that it's working when we have a proper amount of characters.
00:13:05 So now that the UI for the answer is here, what do you say that we clean up some of these unused imports?
00:13:12 First of all, we want to use the form message.
00:13:14 I think I forgot about that.
00:13:16 It's coming right below this form control.
00:13:18 We want to render a self-closing form.
00:13:23 message in case there are some validation issues there we go answer has to have more than 100 characters now we at least know we also have two of these
00:13:33 unused state setters which we'll use very soon we don't have a need for za type here nor these additional values coming from react hook form nor the router
00:13:42 so we can clean these up and we have a very nice minimalistic answer form Hopefully the part where I took my old component,
00:13:52 which was the odd form, and transitioned it over to the answer form wasn't too confusing.
00:13:57 If it was, and if you'd like me to start creating all of these components from scratch, just let me know.
00:14:03 But if there's a lot of shared functionality, I often like to just reuse it to make her life a bit easier here.
00:14:09 Now that we know that we have a beautiful form UI right here, let's go ahead and submit it by saying something like implement a create answer form,
00:14:20 commit and sync.
00:14:22 And in the next lesson, let's develop a server action that allow us to submit the answer that we typed right here.