Create and Use a Layout with Remix Pathless Layout Routes

Ian Jones
InstructorIan Jones
Share this video with your friends

Social Share Links

Send Tweet
Published 3 years ago
Updated 2 years ago

To further prep for implementing user sign up and log in, you will create a sign up and log in page. To do this, there are a few development workflows that you’ll be introduced to. You will create a reusable button component from the previous button that was created to use inside the Nav header.

To actually reuse the buttons, you’ll need to create a nav. One thing that you shouldn’t do is apply a nav to every page you create. This is why Remix has layout routes and in this case because it’s an application layout, you will want a pathless — pathless meaning it won’t show up in the url — layout route. This is denoted with two underscores in the layout file, for an app that is __app.

Along the way you will be introduced to an awesome component pattern that implements an as prop. What this will do is let you define your button AS a link so that you can use actual HTML links instead of buttons for accessibility reasons.

Instructor: [0:00] You are going to build UI components that are required for authentication, components like UserForms, a reusable button, and a nav component. You already have a button in the POST form, so you can extract it into its own component to use elsewhere. Copy the JSX and move it over to a button component file. You can remove the submit prop from the button because not all buttons will be in a form. You also don't want to hard-code the text of the button because each button will have its own children. Now, create a types file and export the type called props. Props will be a component without ref passing in any. Just like all the other components, you can add an index.tsx file and export the button from there. Now, you can go back to the POST form and use the button that you just created. You will notice that the type prop is on the button. The button isn't passing along any other props explicitly. Let's fix that. Here, you want to make sure that you pass the rest of the props to the button. This will allow the user of your button component to pass any props to the button. When you inspect the button, you can see its type is submit. Another case you need to solve for, what if someone wants to add a className? Currently, they will override all of the default classes. You can use a package called classnames to resolve this issue. I have enjoyed the utilities that classnames provides in the past. It will help you avoid applying undefined as a class and has some other helpful utilities we won't be going over. Back in your code, you can import cx from classnames. Go ahead and pass the Tailwind classes as the first argument and classname as the second. If the user defines a className prop, classnames will add that to the class string, but if the user doesn't, you won't have to worry about an undefined class being added to your button. Back in your POST form, you can add something like a border to the button, and a class will be applied. Now that you've extracted a button to use in the application, it's time to create the signup page. Remix uses file system routing, so any file created in the Routes folder will be added to a path in your application. I'll paste in the signup page code, and you can see it's a simple page with an h1 and a UserForm component. Head on over to the Components folder, and you can create the UserForm. The UserForm has a lot in common with the POST form. You can see that it takes an error and field prop. These will be exactly the same as you saw in POST form except the fields and field errors will be an email and a password. The first difference you'll notice is the useTransition() hook. useTransition() is a hook from Remix that provides the state of the form. The state can be one of three things, idle, submitting, or loading. The form starts in the idle state. When you click Submit, it transitions to submitting. Finally, if you are transitioning to another page that loads data, the form will be in the loading state while this is happening. You will also notice that there is a capital-F form on the page. This is required to get useTransition() to work. You will see that the button text changes based on the transition state. The button will also be disabled if the state is not idle. Everything else in this form is something you've seen before. There's an email and password field. They both conditionally render an error if it is passed through props. You can add the types. These will look very familiar. You have error and fields, which are both similar to the POST form, but now, you also have form props, which are just generic form element props. Next, export the component from the components index file, and when you save, you'll see the form render on the signup page. It looks like there was an error importing the UserForm component. When you fix that, the UserForm will show up. Now, you can use the same component for the login page. When you navigate to login, you can see the same form but with a different h1. Now that the authentication pages are set up, it's time to add a header to the application. You don't want to have to add a nav header to every page in your application. This would get tedious and time consuming. Remix has a feature called Layout Routes that lets you nest routes inside of one another. You don't want to add any path segments to your route, though. You can use a feature called Pathless Layout Routes. These routes are denoted with two underscores. You can create an __app file. With this pasted-in code, you can see the outlet component. Whether you know it or not, you've already been using an outlet component in your root.tsx file. Your whole application renders inside of this outlet component. The outlet will render any matching sub-route. Because this is an application layout, it will be rendered on every page. Then this specific page will render inside of the outlet component. To get this working, you have to add a folder with the same name as __app. Next, move all of your routes into __app. Reload the browser. You'll see a header and a little spacing because of the CSS in the app layout. Now, it's time to create a nav component. Here, I pasted in the nav code. You'll notice that there is a Remix link tag being used. Links have some nice features you can use, such as the ability to prefetch the page that it links to. By default, a link will not prefetch a page. Inside of the nav component, there's the name of the application and buttons to either log in or create an account. Notice that the button is taking in as prop. The as prop lets the user of the component designate what HTML tag the component renders as. This is nice for something like a button because you may want button styles for a link, but you don't want to use a button HTML element for accessibility reasons. Before you go implement the as prop on the button, export the file from the index component file. This hasn't been implemented yet, so when you click the nav buttons, nothing happens. Over in the button component, you can add an as prop. This will default to a button element. You can assign the as prop to a component variable. The capital letter signals the user and to JSX that this is a React component that needs to be rendered. Now, when the nav passes the button component and as equals link, the link component will be rendered, and you can pass whatever props that a link component takes. Go ahead and update the types to accept an as prop. Now, when clicking the nav buttons, they act like an anchor tag. To review, you extracted a button component from the POST form. You then used the button in the UserForm much like the POST form. You created a UserForm with an input of email and password, displaying any errors that may show up. You added the new useTransition() hook from Remix, which gives you the state of the form and allows you to render content based on the state of the form. Next, you created a signup and login page. Both render the UserForm. Then you created a routeless layout component. The layout component wraps your app in a div and gives some padding, renders a nav, and then an outlet renders the content of the