Building the Authentication Form
Create the AuthForm component to handle the login and register flow
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the Sleek Next.JS Applications with shadcn/ui course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Sleek Next.JS Applications with shadcn/ui, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:26] In the previous lesson you built the login button and used it to guard the RSVP button for logged out users. In this lesson, you will create your first form with shadcn/ui, the auth form component, which handles the login and register flow. shadcn/ui offers a full form component that are built on top of React Hook Form and Zod, and by the end of the lesson you will be able to craft new form with it yourself.
[00:27 - 01:11] React Hook Form is a library to build forms in ReactFest. It started as a React only library, but over the years it gained popularity, and it's now available as a generic and framework diagnostic hook form library, which powers the React Hook Form library itself. React Hook Form is packed with features like Easy Integration, Performance, and Validation. Zod is typescript first, schema declaration and validation library. Given schema definition, Zod will create a type that matches the schema and allow you to validate some input against the schema . It's a great fit for React Hook Form as it allows you to define your form schema and use it to validate the user input.
[01:12 - 01:55] Here's an example of Zod. We define the schema, username is a string, and password is a string, but with a minimum of six characters, then we can infer the type script type out of the schema using the Zod infer function. We can then define a user object, which is from the user type, and we can call the parse function, which will validate the user value against the schema. If the password was less than six characters, for example, the schema will throw a return an error. Here is what the authentication form looks like. Let's start and implement that. Create a new file in the components folder and call it "auth form".
[01:56 - 02:12] Start by installing the shadcn/ui component we will use. Npx shadcn/ui at latest, add form, input, tabs and sonar.
[02:13 - 02:44] The first step is creating the Zod schema. Import Z from Zod and create a form schema. We call the object function from Zod. The username is a string, and the password is a string as well, but with a minimum of six characters. We will use the same schema for the login and register component, so we can define a single schema for now.
[02:45 - 03:50] The username will be required since we haven't called the optional function. Thanks to the shadcn/ui form components, error messages are displayed when the user input doesn't match the schema you'll see it in a minute. Now create the auth form component, which will wrap all the forms with tabs. So import the tabs component, import the tabs list, the tabs content, and the tabs trigger, and export the const call auth form. It doesn't receive any value and it can immediately return, and wrap everything with the tabs component, pass a default value to it, which is register, and give it a full width. Now render the tabs list. We want to make it agreed with two columns, so class name will grid with full and grid calls to, and then tabs trigger.
[03:51 - 04:48] With the register value, the trigger is the button, pass a register text as well, then a login one, they should be lowercase, and as a sibling of the tabs list, create a tabs content component with the register value, and the second one for the login value. Inside the content, we want to render a card, so we import that, render a card header, the first card will render a register form, we can add a card description as well, and then a card content component that will simply render the register form.
[04:49 - 05:12] We'll implement the register form in a second, you can now copy and paste this card into the login, tab, add the login label, and change it to login form. Now you can create the login and register form. They are fairly simple, as they are both using shadcn/ui form and input component. Let's start with the register form.
[05:13 - 06:10] Export const ReigsterForm, it doesn't receive any props, and in the first line , you call the use user hook to get the register function. Add an import to that, then you initiate the use form hook with the form configuration object, use form, which is a hook form react form, and we're going to pass as z.infer fair, type of form schema, and then we can call the function, then you pass a resolver property, which defines the schema for the react hook form, it integrates between the Zod and react hook form and we are going to pass default values, which are empty user name and empty password.
[06:11 - 07:10] Now we can call the register function inside the unsubmit handler. The values are the same as the form schema of the unsubmit function, and we expect to receive a user back from the register function. If there is no user, we want to set an error inside the form at the root level of the form, and pass a message object that shows an error to the user. We also want to return if that's the case. Now if everything was successful, we want to show a toast to the user.
[07:11 - 07:51] For showing a toast, we need to render the toast provider in the layout of the application, so go to the sonar component first from sheds in UI, and add a user client that react to on top. Then go to the layout dot sx file, import the toaster component, and render it under the theme provider. Now every time we'll call the toast function, the toaster component will render the toast. Now the unsubmit function is complete.
[07:52 - 08:55] Now let's create the form UI, render the form component, and pass the form object to it as a spread object. Under the root, sheds in UI form component is actually the form provider of react to form. Let's see it imports the form provider from react to form, and it's actually even setting it directly to the form provider, so we just wrap around it. The form provider is a context that will set the form context to all its children. Now under it, render an HTML form, with the unsubmit function that you've created will call the form dot and else submit. It will trigger all the validations and other stuff that are related to react to form, and we call the unsubmit function. We also want to pass it a class name, with space-y-8. Inside the form, you will render the form field component.
[08:56 - 10:21] The form field component from the UI form file. Form 3 itself is also a provider of react to context, but a different one, a specific one for the form field. It renders the controller component, which bridges between non-native inputs and custom components with form control functionality. It receives a name property, so let's pass it a name. Here we'll have the username, and it receives a render prop, which returns us the field, and we can use it to render the field. The field object holds all the functionality around the form field, like its value and the own change function. You should either pass it to the input component, the native one, or use it to create a custom input component, for example, a color picker. Now inside the render prop, let's render shadcn/ui for item, and a form label, with the username inside of it, a form control, which is a wrapper around the input, we'll use shadcn/ui input as well, and let's add a placeholder, and pass the field object inside the same way that we did for the form component.
[10:22 - 10:58] Now we can add the form description, where the text below, this is your public display net, and we also want to pass the form message component. The form message is a spatial component that shows error to the user if there was one. All of those components use the form field context, then encapsulate the field object, and provide a nice form field developer experience. You can customize them and move them however you'd like, by passing a class name, for example, or by moving their structure.
[10:59 - 11:27] We can render the label at the bottom, for example. Now let's add the password field, it should look pretty similar. We want to add a type password to the input, and remove the placeholder, and we can remove the description.
[11:28 - 12:32] Now add a submit button, make it type submit, and pass a disabled property which is equal to form, but form state, dot is submitting, so the user won't be able to submit once the form is loading , and add a submit text inside. Great, the login form looks pretty similar with minor differences in the name and the API code, so I'm just going to copy and paste it. The main difference is that we also need to add the close and the form message to the register function, and the main differences are in the login function that is being called inside the unsubmit, and the field are pretty much the same. Now the auth form component is ready to be used inside the login button, navigate to loginbutton.tsx and import the auth form component.
[12:33 - 12:59] Then we render it inside the dialog content instead of the current empty div, and let's see that in action. Run the app, make sure that it's running. Again, if you have an issue running the app, feel free to jump on the discord, and open it on the browser. Click on one of the events, and click the Rs with the button.
[13:00 - 13:16] You can now see the authentication form. Try to create a user, I'm passing a sh ud scene, user name, and then let me try to pass an invalid password with less than six characters. If I'm clicking submit, I should see an error message, string must contain at least six characters.
[13:17 - 13:35] Let's pass a valid password, and I'm now logged into the app. If I click Rs, VP nothing should happen because we haven't implemented it yet. Now let's log out. We'll implement a logout function in the next lessons, but I'm going to remove all the tokens.
[13:36 - 13:47] Refresh the page, and let's try to login with the same user. We can see that the login form doesn't appear. Let's go back to the code.
[13:48 - 14:01] Open the off-form component. There was a typo on the tabs trigger. Go back to the app, and now we can see the login form.
[14:02 - 14:32] Creating a new user, logging out again, refresh the page, and then I'm trying to login with the same user, and I was able to login. I also see a toast coming from the app. In the next lesson, you'll finish the login flow by completing the login button and adding it to the site header.