
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 walk through the process of creating a video upload page using React. The tutorial covers setting up the components, state management, and implementing form functionality necessary for a smooth user experience during video uploads.
00:00:02 To get started working on the upload page, let's create it.
00:00:06 You can do that by heading over to app, root, and creating a new folder, which I'll call upload.
00:00:14 And within it, I'll create a new file called page.tsx.
00:00:20 Within it, we can run RAFCE just to quickly spin it up.
00:00:24 And once you do that, you should be able to click the Upload a video button and you'll be redirected to this newly created page.
00:00:32 Within this page, we'll use a couple of reusable components, such as the file input and different form fields.
00:00:40 So let's create those two first.
00:00:43 I'll create them right here within the components folder by creating a new component called formField.tsx.
00:00:53 And I'll run RAFCE.
00:00:55 And we also want to create a file input.
00:00:58 So I'll create it right here by creating a new file input.tsx.
00:01:03 This is going to be for the video upload.
00:01:06 And I'll also run RAFCE.
00:01:08 So now that we have these two new files, we can just import them within the page.
00:01:14 I'll first give it a class name equal to wrapper and page.
00:01:19 And here I'll display a single form field and another file input.
00:01:27 If you do that, you should be able to see two components, one that says form field and the other one that says file input.
00:01:34 Now we can start creating this page.
00:01:36 In this case, I'll provide it a wrapper MD instead of a typical wrapper because we want to keep the form right in the middle of the screen.
00:01:45 And I'll also give it the upload page class name and right within it, I'll display an H1 that'll say upload a video.
00:01:55 Perfect.
00:01:55 Later on, we also might have an error.
00:01:58 So if there's an error, we want to display it at the top of the page.
00:02:02 So right at the top, I'll say use state snippet, and I'll create a new one called error, set error at the start set to null.
00:02:12 Of course, we need to import use state from react.
00:02:16 And alongside that, we have to make it a client rendered page by giving it use client directive at the top.
00:02:23 Now, right below the H1, I can say if there is an error, in that case, just render a div that'll have a class name of error-field,
00:02:34 and it'll simply render the error text.
00:02:37 So we can close it right here.
00:02:38 Perfect.
00:02:39 But now, since we don't have an error, we can start focusing on the form.
00:02:43 I'll first create a new form component that'll have a class name equal to rounded-20.
00:02:51 shadow-10, and already you should be able to see something that is starting to look like a card with a gap of 6 in between the elements,
00:03:00 w-full, flex, flex-column, padding x of 5, and padding y of 7.5. So now that we have given it some height, you can start seeing it as a card.
00:03:13 And now we can put these form fields and form inputs right within this form.
00:03:18 where they belong.
00:03:19 There we go.
00:03:20 Now, we have to ask ourselves, what different types of fields or props do we want to pass into each one of these form fields?
00:03:29 And of course, to be able to answer that question, we have to check out the design for the form.
00:03:34 you'll see that we'll reuse this form field for the title and the description, maybe even the thumbnail we'll see about that,
00:03:43 but definitely for visibility as well.
00:03:45 So we'll surely reuse it a couple of times.
00:03:48 And what changes is the name of the input or the title, or we can also call it a label, maybe a placeholder, here it says describe what the video is about,
00:03:58 and here it says select visibility.
00:04:00 So let's create our first form field by passing the right props in.
00:04:04 This time for the title.
00:04:06 So what do we need for the title?
00:04:08 Well, we need the ID equal to title, a label equal to, let's do title.
00:04:15 We'll also pass a value equal to, and now we have to ask ourselves, how are we going to handle the state?
00:04:23 You could use a library like Schatzian, but in this case, I'll just go ahead and create a new state field by saying use state snippet.
00:04:33 I'll call it form data and set form data at the start equal to an object where we want to set all of these values to their empty strings.
00:04:43 So I'll say title is an empty string.
00:04:46 Description will also be an empty string.
00:04:49 After that, we'll have the visibility.
00:04:53 And I think this is it, at least for now.
00:04:56 By default, visibility can be set to public, maybe.
00:04:59 So now, if I scroll down, we can set the value to be equal to formData.title.
00:05:07 And we also need to give it an onChange.
00:05:10 And in this case, we need to call a handleInputChange function.
00:05:14 So right here.
00:05:16 I'll create a new handle input change, which is going to be a function that accepts the E as an event, like a key press event.
00:05:25 And this one will be of a type change event coming from React.
00:05:30 And within it, we need to update the form data.
00:05:33 So first things first, we can extract the name as well as the value from the target.
00:05:40 What are we doing here?
00:05:42 Well, the name is going to be the name of the input that we're currently modifying.
00:05:46 In this case, it'll be the title.
00:05:48 And then the second thing is the value.
00:05:50 And the value will come as part of the onChangeListener.
00:05:53 When we press a keystroke, we will just count it up right here.
00:05:57 So what do we do with the name and the value?
00:06:00 We take the form data.
00:06:02 And if you're modifying the form data with the previous version of that same state, you have to get the previous state.
00:06:11 And once you get it, you can automatically return with parentheses a new object where you can spread the previous state and then dynamically modify the
00:06:22 new property with a new value.
00:06:27 That'll look something like this.
00:06:29 So spread the previous state, for example, if we had already a state that looks like this right here.
00:06:36 There we go.
00:06:37 Now what you want to do is maybe update the description, so it'll spread these fields, and then it'll add a new field with a type of name and a specific value.
00:06:49 This dynamic name property will be updated to description, because that's the field that we're typing in, that's the name of that field,
00:06:57 and then the value will be the keystroke that we press in, like ABC.
00:07:03 And that's how we're going to update the value in the state.
00:07:06 So within here on change, we simply need to call the handle input change, just like this.
00:07:16 Perfect.
00:07:17 And let's also provide it a placeholder equal to enter a clear and concise video title.
00:07:26 Perfect.
00:07:27 Handle input change is always going to be the same, but the ID, label, placeholder, and the value change, so I'll leave those right here.
00:07:35 Of course, now that you created this, I mean past the props, nothing is really happening.
00:07:40 the original form field component is still the same as it is.
00:07:43 So we have to head into it and accept those props right within it.
00:07:48 So I will destructure the props for passing, such as the ID, label, maybe type.
00:07:56 Later on, we'll have a type of select, but by default, it can be a type of text, a value, an onChange, a placeholder, and also as.
00:08:07 So we can say as by default, we can render it as input, but later on for the select, we can pass select.
00:08:14 And if it is a select, we'll also have to pass some kind of options.
00:08:18 So by default, I'll set the options to be an empty array.
00:08:22 And these will be of a type form field props.
00:08:26 Perfect.
00:08:27 So now that we're getting all of these right here, we can give this div a class name equal to form field, and we can give it a label.
00:08:37 This label will be an HTML4, the ID element.
00:08:42 And it'll render the label that we're passing into it.
00:08:45 Right here, it says title.
00:08:46 Finally, we have to decide what are we rendering it as.
00:08:50 So one of these will be a title, regular input.
00:08:53 The second one will be a select and the third one will be a text area.
00:08:57 So what I'll do is create a new functional component within it and say, const.
00:09:03 inputToRender is equal to, and now we have to decide what we are rendering.
00:09:09 So this inputToRender will take in the type of the input, and then we can decide what we want to do.
00:09:14 So I'll say if type is triple equal to text area, In that case, we can just return a new text area element that looks something like this.
00:09:26 Else if the type is triple equal to select.
00:09:30 In that case, we can just return a new select element, which looks something like this.
00:09:36 And finally, else we can return a regular input.
00:09:40 So right here, I'll say return input.
00:09:43 So how do we actually call this component?
00:09:46 Well, we can just call it like this, input to render.
00:09:48 And to it, we can pass a type.
00:09:52 which is basically the as property we're passing into it.
00:09:56 It'll be of a type string, but since we're rendering it as a component, we have to actually destructure it from props because we're passing it as a prop
00:10:04 right here.
00:10:04 So I'll say type is an object and that type will be of a type string.
00:10:11 Perfect.
00:10:12 So now we are rendering the label for every single one, but then depending on the as property, we're going to render different things.
00:10:21 So if I head back into the page, I will duplicate this form field once again, right here.
00:10:28 And for the second time, I'll give it a ID of description, the label of description as well.
00:10:36 A placeholder here, I'll say, describe what this video is about.
00:10:43 And of course, let's not forget to change the value to description.
00:10:48 And finally, give it the as property, text area.
00:10:53 So now if you render it, you can see that the first one is an input and the second one is a text area.
00:10:58 But it's still the same form field component.
00:11:02 So we're really doing a great job of making sure that this component is reusable.
00:11:07 And the third type is going to be right below the file input.
00:11:11 So if I head over to the page, Below the file input, we'll actually have two file inputs, one for the video and the other one for the thumbnail of the video.
00:11:21 So below it, I will duplicate this form field and give it an ID of visibility, a label of visibility as well.
00:11:30 In this case, we don't need a placeholder.
00:11:33 The value will be form data visibility as select.
00:11:38 And in this case, we also have to give it options.
00:11:41 And the two options will be a value of public and a label of public with a capital P.
00:11:52 And as the second option, we can just render private.
00:11:55 So here I'll say private and I'll render the label of private.
00:12:01 So now we have three different form fields, each serving as a different HTML element.
00:12:06 The first one is an input, the second one is a text area, and the third one is a select.
00:12:12 So now if we head back over here, we also need to pass some additional props to these HTML elements themselves.
00:12:20 For example, some of them are going to be shared across all of these different elements.
00:12:25 So I'll select all three and pass them an ID equal to ID.
00:12:32 I'll also give them a name equal to ID.
00:12:36 And I'll give them a value equal to value.
00:12:39 And then onChange equal to onChange.
00:12:43 Now for the text area and the input, we also need to pass it a placeholder equal to placeholder.
00:12:50 There we go.
00:12:51 You can see that now we got those two placeholders.
00:12:54 And for the select, we actually have to give it some additional options.
00:12:59 So I will turn it from a self-closing to a not self-closing component.
00:13:04 And within it, I will map over options by getting each individual option.
00:13:09 And for each one, we'll return an option component that will display the label, which we can destructure from the option right here.
00:13:19 So destructure the label as well as the value.
00:13:22 If you do this, you can see that now we have two of these, but we also need to give each one of these options a key, since we're mapping over it,
00:13:30 of label.
00:13:32 as well as the value equal to value.
00:13:35 So if you save it, you can see that now we can actually select private, public, we can type into the text area, and we can type into the title,
00:13:44 but it kicks me out.
00:13:46 We'll fix that later on, but for now what matters most is that we have the UI of three of these components, which seemingly are the same form field,
00:13:55 But with different props that we give to it, we make it that much more reusable.
00:14:00 And now that our form fields are done, we can also focus on the video and the thumbnail upload.
00:14:05 These are a bit more custom and they'll have to deal with a lot more functionality, but let me show you how we can implement them.
00:14:13 Once again, ask yourself, what are the different props that we need to pass into it?
00:14:19 Of course, we'll need an ID.
00:14:21 So I'll say ID is equal to video in this case.
00:14:25 Label will be equal to video.
00:14:27 Now file inputs also have the accept prop, which allow you to define which type of a file you want to allow the users to upload here.
00:14:36 So we'll say all video types.
00:14:38 And now we also have to store this video somewhere.
00:14:42 So for the time being, I will create a dummy variable called video, which is going to be equal to an empty object.
00:14:50 Later on, we'll get this video by uploading it over to Bunny, but for the time being, we just need the video variable.
00:14:58 And from that video, we can then extract many other things, such as the file will be equal to video.file.
00:15:06 The preview URL will be equal to video.previewURL.
00:15:11 The input ref will be equal to video.inputref.
00:15:17 The onChange will be equal to video.handleFileChange.
00:15:23 on reset so if we want to reset this form we'll say video.reset file and the type will be video so we're going to be getting all of these from this video
00:15:35 object that we'll be getting over from bunny now alongside this video we'll also need to do the same thing for the thumbnail so i'll say const thumbnail
00:15:45 is equal to an empty object.
00:15:46 And then if we go down, I can copy this entire file input and duplicate it instead of this empty one.
00:15:54 And we're going to change this over to thumbnail.
00:15:57 Label will be thumbnail as well.
00:16:00 We will accept images for the thumbnails.
00:16:03 And then everything else will remain the same, but I'll change every mention of video to thumbnail.
00:16:09 And at the end, I'll set the type to be equal to an image.
00:16:14 And once again, if we now save this, nothing will have changed.
00:16:18 That's because we now have to go into this file input and implement this component.
00:16:24 So everything starts with first accepting those props, such as the ID, the label, the accept property, file, preview, URL.
00:16:36 Input ref, onChange, onReset, and the type.
00:16:42 And these are of a type, fileInputProps.
00:16:48 And we can return this component as a section because it'll actually contain multiple things within it.
00:16:54 But I'll give it a class name equal to file input.
00:16:58 Now this section will have its own label.
00:17:01 So I'll say this is a label with an HTML4 ID and it'll render the label within it.
00:17:10 I'll render an input.
00:17:12 which will have a type equal to file, an ID equal to ID, accept equal to accept, because these file inputs by default are very ugly and look like this.
00:17:24 But we want to create a full display that allows us to nicely drag and drop the files into it.
00:17:30 I'll also give it a ref equal to input ref.
00:17:34 Oh, and I think I forgot to hide it, so I'll say hidden.
00:17:37 And I'll pass it the onChange, equal to onChange.
00:17:43 So if we do this, you'll see that it appears like they're not here.
00:17:47 But only now will display the actual presentation of where we want to put those files in.
00:17:53 That is why we need to attach a ref to this input so that we don't interact with it directly because it's hidden, but actually design a new element that'll
00:18:04 allow us to upload either a video or a thumbnail.
00:18:08 So I can use the prop called preview URL for that.
00:18:11 So I'll say if there is no preview URL yet, in that case, we'll render a new figure within which we'll render an image with a source of assets,
00:18:23 icons.
00:18:24 upload.svg and an alt tag of upload, a width of 24, a height of 24 as well, and below it we can render a p tag that says click to upload your ID.
00:18:41 The ID is either image or a file.
00:18:44 So this is if we haven't yet uploaded anything.
00:18:47 But if we have, for now, let's just display an empty div.
00:18:51 Okay, so if you save it, you should be able to see that now we have Click to upload your video and Click to upload your thumbnail right here.
00:18:59 But right now, they're not really doing anything.
00:19:02 So let's focus on the second part, which is when we upload a video, we need to display that video's thumbnail or the thumbnail itself.
00:19:09 So we can do that right here.
00:19:11 Within this div, if the type is triple equal to video, in that case we can just play that video.
00:19:19 Video, give it a source of preview URL, and give it some controls so people can see what they uploaded.
00:19:28 Or if we're not uploading a video, that must mean that it's an image.
00:19:33 So in that case, I'll render an image with a source of preview URL with an alt tag of image and give it a property of fill so it fills up the entire space.
00:19:44 If you do this later on, once we upload it, you'll be able to see the preview.
00:19:48 And below, let's also render a button with a type is equal to button with an on click doing the reset.
00:19:57 So I'll say on reset.
00:20:00 And this is exactly what that button will do.
00:20:02 Render an image with a source of assets, icons, close.svg with an alt tag of close, a width of 16 and a height of 16 as well.
00:20:17 So in case you want to just reset that image or upload another image or a video, you'll be able to do that after seeing your current image or video.
00:20:26 And below that button, let's also display the current file name so we know what we have uploaded.
00:20:33 Perfect.
00:20:34 So with this in mind, we now have these two file inputs and the three other inputs.
00:20:39 So let's actually allow for the upload.
00:20:42 Right here within this figure, I'll give it an onClick property.
00:20:46 And onClick, we simply want to trigger the click on the original file input.
00:20:52 So we're going to get access to its ref by saying input ref dot current dot click.
00:21:00 So we're basically clicking that hidden ugly input when we click on this new one that we have created.
00:21:05 So if I click on it right now, you can see one issue, and that is that input ref current is undefined.
00:21:13 So let's make sure that this input ref is actually being passed through the file input right here.
00:21:20 And of course, right now, it definitely is not, because this thumbnail and video objects are completely empty.
00:21:27 So to be able to set up the starting point of this video and the thumbnail, We need to figure out a way how we can handle the upload.
00:21:36 Everything from making sure that we have a ref that we can click on, to making sure that we know what it is that we're uploading,
00:21:43 whether it's a video or a thumbnail, all the way to keeping track of the duration of the uploaded video and making sure that we get access to the preview URL.
00:21:53 All of these different things that we have here matter.
00:21:56 So let's take that logic and let's put it within a new file.
00:22:00 Let's create a custom hook that only deals with the file upload logic.
00:22:06 Okay.
00:22:08 So I'll create it within a new folder within lib and I'll call it hooks.
00:22:17 Within hooks, I'll create a new hook called useFileInput.ts.
00:22:25 And here we can start by creating this new hook.
00:22:28 I'll start by creating and exporting it by saying exportConst useFileInput is equal to a function that accepts the max size of a file that we want to allow
00:22:41 for the upload.
00:22:42 And then it will return everything regarding that file.
00:22:46 So this custom hook will keep track of all of the different important states, starting off with the file itself.
00:22:54 So I'll say use state snippet, and it'll be equal to a file, set file at the start equal to null.
00:23:02 And of course, don't forget to import the useState from React.
00:23:07 Next, we can also deal with the preview URL.
00:23:09 So I'll say useStateSnippet and I'll name it previewURL.
00:23:14 setPreviewURL at the start equal to an empty string.
00:23:19 We can also do another one for the duration.
00:23:21 So I'll say useStateSnippet duration and setDuration at the start equal to zero.
00:23:29 And we can also do one for the input ref.
00:23:32 So I'll say not use state, but rather use ref.
00:23:39 And I'll call it input ref is equal to use ref at the start equal to null.
00:23:45 Don't forget to import use ref coming from react.
00:23:50 Perfect.
00:23:51 So now I think you can start seeing why we need to define a custom hook.
00:23:56 Because if we didn't, we would have to call all of these states within the form and we would have to do it one more time for the thumbnail and for the video.
00:24:06 So it's much better to define a custom hook that encapsulates all of this logic.
00:24:11 This custom hook has to keep track of what the file is doing.
00:24:17 So whether we're uploading it or not.
00:24:19 So say const.
00:24:21 handle file change is equal to a function that accepts the event of a type change event, and we can open up a function block.
00:24:32 We can call this function when we actually submit a file to be uploaded, so we first have to check.
00:24:38 If e.target.files 0 exists, then that means that we have a selected file.
00:24:46 So let's say const selected file, is equal to E.target.files 0. Right now, it does say that files might not exist on the target,
00:24:56 and that's because it doesn't know what kind of an event it is.
00:25:00 In this case, it is a HTML input element change event, so now it knows that files might actually exist on it.
00:25:07 Now that we have the selected file, we want to check whether it's of a right size.
00:25:11 So if the selected file dot size is greater than the max size that we'll allow, in that case, we can simply exit out of this function,
00:25:21 which means return.
00:25:22 But if everything is going well, in that case, we can check whether a preview URL exists.
00:25:29 And if it does, then we can revoke it.
00:25:32 So I'll say URL dot revoke.
00:25:36 Object URL, preview URL.
00:25:40 Why are we doing this and what does this mean to revoke an object URL?
00:25:45 Well, it means that we want to call the browser to let it know not to keep the reference to this file any longer, because we're done with the preview and
00:25:53 we have the access to the actual file.
00:25:56 So I'll say set file is equal to selected file.
00:26:02 So we're setting it to the state.
00:26:03 And at the top, we can say that this state is of a type either file or null.
00:26:09 So it knows that we can actually pass a file to it.
00:26:12 Then we get the object URL, which is equal to URL dot create object URL.
00:26:20 And now we pass the real selected file that has been uploaded.
00:26:23 And we set the preview URL to be equal to that object URL of a real file that got uploaded.
00:26:30 Finally, we have to extract the duration of the video.
00:26:34 So I'll say if selected file.
00:26:37 dot type dot starts with and that's going to be video.
00:26:42 If that is the case, then we want to get access to that video by saying const video is equal to document dot create element of video.
00:26:54 Then we can preload the metadata of that video by saying video dot preload is equal to metadata And then we can say video.onload metadata,
00:27:06 which is a listener.
00:27:09 Once it actually loads, we can check whether a duration exists.
00:27:13 So I'll say if is finite.
00:27:16 So that means that it actually has the end of video.duration.
00:27:21 And if video.duration is greater than zero, in that case, we simply want to set the duration to be equal to math.round video.duration to round it to the
00:27:36 nearest integer.
00:27:38 Else we're going to simply set the duration to be set to, in this case, we can do zero.
00:27:44 Finally, we can run the URL dot revoke object URL of the video source, because we have already saved it to the state.
00:27:52 So we can say video dot SRC is equal to the object URL that we have created right here above, which contains the actual file we have uploaded.
00:28:01 There we go.
00:28:02 So this single function within this hook deals with the file upload logic.
00:28:08 And alongside the handle file change, we also need to be able to reset the file in case we want to delete it.
00:28:15 So I'll say const reset file is equal to a callback function.
00:28:20 We can again check if we have access to a preview URL.
00:28:23 And if we do, we can just revoke it.
00:28:25 We don't need it anymore.
00:28:27 So URL dot revoke object URL of preview URL.
00:28:32 And we want to set file to be equal to null.
00:28:35 We can set preview URL to be equal to null as well, because it doesn't exist.
00:28:40 Or in this case, I think we can set it to an empty string.
00:28:43 And we want to set the duration to be set to zero.
00:28:46 So we're resetting all the values.
00:28:49 And if inputRef.current, so if an input exists, we just want to clear it by saying inputRef.current.value is equal to an empty string.
00:29:01 In this case, it's saying that the value might not exist, so we have to say that this ref will specifically be used on an HTML input element so it knows
00:29:10 that a value does indeed exist.
00:29:12 So now that we have the reset file and the handle file change, alongside all of these different states, We can now return it from this hook by saying return
00:29:23 inside of an object, and we can pass everything.
00:29:26 A file, a preview URL, a duration, an input ref, a handle file change, and reset file.
00:29:36 We're returning all of that from this custom hook.
00:29:39 just so we can come back and we can use it in a super simple way.
00:29:45 To set up our video and to allow us to upload and change and reset those videos, the only thing you have to do now is say const video is equal to use file
00:29:57 input And that's it.
00:29:58 In this case, we also need to pass the maximum file size.
00:30:01 So I'll say max video size like this, which is defined within the constants.
00:30:07 Here, I think we're setting it about 500 megabytes.
00:30:09 So that's half a gigabyte.
00:30:11 And for the image, about 10 megabytes should be enough.
00:30:15 So now, if we head back, we can do the similar thing for the thumbnail.
00:30:19 I'll say Use File Input and provide a max thumbnail size.
00:30:24 And with this, if you now click on this video, you can see that it'll allow you to upload the videos.
00:30:30 And if within your downloads you have a lot of different files, you'll only be able to upload videos.
00:30:36 In the same way, here, you'll only be able to upload images.
00:30:41 Looks great on mobile, but how does it look like on desktop?
00:30:45 Also great.
00:30:46 You can see here that we centered it in the middle.
00:30:48 We didn't really need more horizontal space for this, so this is good.
00:30:53 And it all seems to be working.
00:30:56 Oh, but when I type something into the title, it looks like it only stops at one character.
00:31:01 So if I come back, let's see what we're doing with this handle input change.
00:31:05 First of all, it doesn't know that a name and a value exist.
00:31:09 So we have to say that this change event is of a type HTML input element.
00:31:16 There we go.
00:31:17 And let's see where we're calling this handle input change.
00:31:20 We're calling it on the change and we're passing it right here to all of these different properties.
00:31:25 But if this is set up well, then why is our input losing focus whenever we type a single character?
00:31:32 So if you check out where we're calling the onChange, that part is looking good.
00:31:37 It doesn't have anything that would make the input lose the focus.
00:31:41 So let's head over into the form field.
00:31:44 And if you take a look at it, we are returning a new component on every single render of the form field component.
00:31:52 So as we type into it, React sees it as a brand new component every time you type, unmounting the old one and mounting the new one.
00:32:00 Hence the focus gets lost.
00:32:02 So what we need to do is just render these three types of inputs in line.
00:32:07 We can do that with a simple ternary operator where I'll say dynamic block of code as is triple equal to text area.
00:32:17 If that is the case, then we just render the text area.
00:32:22 Next, if it's not the text area, then if as is equal to select, in that case, we can just return the select.
00:32:32 So I'll open up another block of code and return the select.
00:32:37 And finally, if it's neither the select nor the text area, in that case we can add a question mark here to open up a secondary ternary,
00:32:47 and then finally we can return the regular input.
00:32:50 So let me just structure this nicely and make sure that we have three separate outputs.
00:32:56 And now the only thing you have to do is close the dynamic block of code and then copy it from here.
00:33:02 Remove the input to render and just paste it right here.
00:33:07 That'll look something like this.
00:33:09 So now you can see that we have the label.
00:33:11 And then if as is text area, we render the text area.
00:33:15 If as is select, we render the select.
00:33:18 And if as is something else, we render the regular input.
00:33:21 And if you do this, you can now notice that it's not losing input as we type.
00:33:26 Great.
00:33:26 So now if we head back over to our page, we can finish it off with a nice button.
00:33:31 Still within the form, I will render a button component with a type is equal to submit.
00:33:38 And it'll be disabled if we're currently submitting.
00:33:41 So we'll say if is submitting.
00:33:44 is true, then we can make it disabled and this is disabled will be just a simple state.
00:33:50 So I'll create a new use state snippet is submitting set is submitting at the start equal to false.
00:33:58 And if we are submitting, it's going to be false.
00:34:01 Also, we can say if is submitting, we can make it say uploading dot dot dot.
00:34:07 Else, we can make it say upload video.
00:34:11 There we go.
00:34:12 Let's also give it a class name equal to submit button.
00:34:17 And there we go.
00:34:18 Now we've got that primary color.
00:34:20 So as you can see, now we have what appears to be our final form.
00:34:24 And this button is submitting the form.
00:34:27 So on the form itself, we have to add what that form will submit.
00:34:31 So on submit, we want to call the handleSubmit function.
00:34:36 So right here below the handleInputChange, I'll create a new const handleSubmit and make it equal to an asynchronous function that accepts an event,
00:34:47 this time of a form event type.
00:34:50 And first things first, we want to prevent the default behavior of the browser.
00:34:56 So E.preventDefault, we don't want the page to reload.
00:34:59 And we want to set isSubmitting to be set to true because we're actually submitting the form.
00:35:04 Then we want to open up a try and catch block.
00:35:08 In the catch, we can get access to the error and we can just console log it.
00:35:12 So we'll say console log error submitting form, and then I will render the error message itself.
00:35:19 And I'll also add a finally clause.
00:35:22 So whether it fails or succeeds, we can stop the submitting.
00:35:27 So I'll set set is submitting set to false.
00:35:31 Perfect.
00:35:32 So now, of course, the main question is, what do we do when we want to submit the video?
00:35:37 Well, before we even try doing that, we need to set some ground rules.
00:35:42 And that is that if there is no video.file or if there is no thumbnail.file, Well, that means there should be some kind of an error,
00:35:52 so I will set error to please upload video and thumbnail.
00:35:59 And we can also just return, so we exit out of that if statement.
00:36:03 Also, this error needs to be a string, not a null, so right here instead of null, by default I'll set it to an empty string,
00:36:11 and then later on we can set it to some other value.
00:36:13 Now another if statement that we want to add is if there is no formData.title or if there is no formData.description.
00:36:25 If that is the case, then we want to also set an error and it'll say something like, please fill in all the details.
00:36:34 And we can also return to exit out of the function.
00:36:37 And now we're finally ready to upload our video.
00:36:41 So there are going to be a couple of steps.
00:36:44 First, we want to upload the video to Bunny.
00:36:48 This is our video streaming and storage platform.
00:36:51 After that, we want to upload the thumbnail over to our database.
00:36:55 Then once we get that image hosted somewhere, we want to attach it as a thumbnail to the video.
00:37:00 So I'll say attach thumbnail.
00:37:02 And finally, once the video and the thumbnail have been uploaded, we can simply get the video URL and call it a day.
00:37:09 We also need to create a new piece of data within our database that will contain all the metadata about the video or video details.
00:37:18 So I'll say create a new DB entry for the video details.
00:37:24 And this contains everything from URLs of the video itself hosted on Bunny to thumbnails and additional metadata.
00:37:32 So there are many different steps that have to happen so we can properly upload the videos and then be able to retrieve them within our application.
00:37:40 So we can conclude this lesson right here.
00:37:43 As we have successfully implemented the UI, as well as some minor functionality of the upload video form.
00:37:50 And in the next lesson, we'll dive deep into setting up a bunny CDN so we can host, stream, and share our videos.
00:37:58 I'm super excited for that one.