Remix is a new web framework from the creators of React Router that helps you build better, faster websites through a solid understanding of web fundamentals. Remix takes care of the heavy lifting like server rendering, code splitting, prefetching, and navigation and leaves you with the fun part: building something awesome!
Building Better Websites with Remix
AI Generated Video Summary
1. Introduction to Remix Web Framework
Hey, everybody! Today I'm excited to share Remix, a web framework built on React Router. Remix provides an awesome experience for developing websites with React, focusing on web fundamentals, accessibility, performance, and flexibility. Let's do a quick demo using our Express starter template, which can be deployed to various cloud providers. Remix is a single programming model that works anywhere. We'll start at the entry points, the client entry point for rendering in the browser, and the server entry point for server rendering. Remix delivers real HTML and SEO benefits using render to string.
Hey, everybody, what is up? My name is Michael Jackson. I am so stoked to be with you here today at React Summit. Thank you so much for tuning in to my talk. I'm excited to share with you the stuff that I've been working on today, specifically, I want to talk about Remix, which is a web framework that I've been building for the last year or so with my business partner, Ryan Florence.
Together, we have been working on React Router for the last five or six years, which is open source software, it's pretty big, it's used by a lot of people around the world to build awesome web experiences. Remix is built on React Router, the awesome stuff we're building for Remix is actually going to make it back into React Router. The idea behind Remix is that we think that there should be an awesome experience for developing websites with React Router, with React, websites that are built on web fundamentals and that are just awesome for people, as far as accessibility, as far as performance goes, and as far as flexibility and power of the tools that you have at your disposal to build really cool stuff.
So I'm really excited to share Remix with you today. And I thought I would do a quick demo. I only have 20 minutes, so this is going to go pretty fast. But I wanted to show you how, you know, give you a little tour of Remix and some of the stuff we've been working on, and show you what it's like to build a little, you know, just a little app with Remix. So I'm actually going to be using our Express starter template. So we have a few of these starter templates. We have some, we have one right now for Express, which you can deploy to any old virtual private server that you want or any, you know, lots of different cloud providers will just run Node apps. We also have one for Vercel. We also have one for architect, which is, you know, running on AWS lambda. We have coming support for Netlify, for Firebase, and Cloudflare worker. So our plan is with Remix to just support anywhere that you want to deploy a web app, because they all have their kind of different, you know, strengths and Remix is a single programming model that can work on any of them. So I'm going to start with our Express starter. I've got a little app here that is actually, I've been working on it just a little bit. It's basically the Express starter, but I've added a few components. I've got Tailwind going on in here, I've got PostCSS going on, and I've got a couple of routes here already. So let's just start right at the top, right at our entry points. So we've got two entry points here in your app folder.
This one is the client entry point, so this is a regular old React DOM hydrate, and the component that we're rendering is our Remix browser component. This is the one that you render when you get to the browser, and so you have full control over your entry point if you wanted to do something globally in the browser, you can go ahead and do it here in your client entry point. In your server entry point, this is usually running in Node, but this is a function that is basically responsible for rendering the HTML. So Remix is fully server rendered React apps, right? You're going to get all that SEO goodness, all of the like, sending real HTML over the wire, we're not just sending like a shell, and then you fill it in later. This is real HTML. So we're using a standard render to string here to render our Remix server component.
2. Returning HTML Page and Route Overview
We're returning an HTML page using the web fetch API in Node. The root route in React Router serves as the single entry point, rendering the entire document. The outlet component is used to render the child routes. Each route can define meta tags for the page. The dev server is running, and we see the welcome to Remix page with a hello world paragraph. We'll be doing an off demo today.
And there were we're returning an HTML page. In this function, you get a request and you return a response. These objects are not things that we invented. These are just from the web fetch API. So even though this function runs in Node, you get to use the familiar fetch primitives that you use on the web.
So those are our entry points for the client and the server. Let's go ahead and take a look at our routes. So you always have the root route. The root route is the single entry point for React Router. So this app actually gets to render the entire document. We saw earlier we were into the document. So here we render the html element, the head element, the body element, we're rendering meta tags and links, we'll take a look at those in just a second. And then we've got an outlet here. So this is the element in React Router V6 that a route uses to render its child routes. So the content of the page, just like the meat and cheese in a sandwich is just gonna go right in there in this outlet component. So that's going these routes, either the 404 route or the index route.
So let's go ahead and take a look at our routes. So you'll notice in our index route, we've got a component here. We also have this meta function. So each route gets the chance to define these are my meta tags that I need. You know, meta information about the page. So in this case, we have the title and the description for this page. So let's go ahead and make sure our dev server is running. So our dev server is already running here. So let's go ahead and fire that up in the browser and see what we get. Okay, so we've got our welcome to Remix page. This is just our hello world. Basically, we're just rendering this little paragraph tag. Let's go ahead and tweak the title Remix. We're going to be doing an off demo today because off is something that everybody has to do on the Web.
3. Styling and Components in Remix
In Remix, we can update meta tags automatically as we tweak them. We import styles using a links function that generates HTML link elements. The index route has meta tags and links specific to it. When refreshing the page, we can see the document and global style sheet loading. By adding link tags to specific routes, we can control which styles are loaded. This helps avoid conflicting styles between different routes. Let's add some cool-looking components, like a hero section.
And so we're going to show you how you do it in Remix. So as you tweak your meta tags, we'll automatically update things like the title and stuff for you. We need to get some styles on here. I told you earlier that we were using some post-CSS. So we have post-CSS Watcher running, and it's just dumping some styles into this directory here.
So what I'm going to do here is I'm actually going to import a URL for that file from that directory. So let's get some global styles on the page. And I'm going to have something here called a links function. A links function is going to be used to generate those HTML link elements, and we're going to put those on the page for you. So our links function is going to return a bunch of links that we want on the page. In this case we want a style sheet. Its URL is that styles URL. So what Remix does is it now knows here's the component that I need to render, and here are also some links that I need to render in the page, just like the index route has some meta tags that it needs to render.
So let's go ahead and refresh this here. So we've got some styles. Now let's go ahead and take a look real quick at this network tab. And we can see when we refresh the page we can see we are getting the document, we're also getting the global style sheet that's loading in here. Let's go ahead and grab these styles, and we're going to go ahead and put a link tag actually in our index route that is going to import not the global styles, but the index styles and that should be good for this. Let's pop back into Chrome here. Let's take a look. So now we'll see that both the global styles and the index styles are loaded in when we're in the index route, but if we were for example at the, you know, 404 route, we'll notice we're just getting the global styles. You'll notice that we got the right HTTP status code for this page. We got a 404 not found. That's important for when search engines come around and they hit this URL and they now know it's a real 404 and not just a 200 that says it's not found. You'll also notice that, so the way that we do the CSS is, you know, when a route is active, its CSS is also active. Its link tags are on the page, but when that route goes away, its link tags disappear from the HTML, so this helps you avoid conflicting styles between different routes. You can still have your styles be, you know, use regular CSS, we'll just add and remove the link tags to and from the page when those routes become active or not. Let's head back to our index route, and this is kind of boring. Let's add some cool-looking components here. Let's get our, grab, I've got a couple of Tailwind components that I'm going to grab here and put on the page, so I've got a hero section.
4. Implementing Login Button and Signin Route
In this part, we implement the login button and create a new file called signin.tsx to handle the sign-in route. We add a login form to the signin page and create an action function to handle form submission. The form currently doesn't do anything, but it has fields for email address, password, and a remember me checkbox.
I've also got a content section, and instead of saying welcome to Remix, let's go ahead and put the hero on there, and we'll put the content section on there. Okay so let's head back into the browser, let's refresh. Okay now we've got a full-blown website, that's pretty good, I can go home, I'm done for the day.
So today what we're going to do is we're going to actually we're going to implement this login button. You'll notice that right now it just goes to a 404 at the sign in route, so let's go ahead and go and make that route a real thing. So I'm going to create a new file here called signin.tsx and we're kind of mimicking URLs with the name of the file. So if I just have a file here called signin and it exports a component, we'll call it signin. And then we'll return a div called signin, something like that. So we'll pop back into our browser and refresh. So we'll notice that this component in the signin.tsx actually renders at slash signin. So it's kind of cool. Let's go and grab a login form. Login form. There we go. Instead of just that div, we're going to return a full blown login form. Here on our signin page, let's go ahead and refresh. All right. So we now have a login form at the signin route. So this form doesn't actually do anything yet, we can't really submit it. But we'll notice there are a couple of fields here. We have an email address, we have a password and then we have this little remember me checkbox.
So what we're going to do is we're going to go back to our route and we're going to add a handler for the submit. So we're going to call this the action function. And the action function is going to get your request. And this one is actually going to be an async function, because what we're going to do is we're going to read the form data, basically, out of the form. Await request.text. You'll recognize that API from the web fetch API. And the request text is actually going to be URL encoded. So we're just going to parse it using URL search params, which is again another web API. It's actually a node.
5. Validating Form Data and Logging in
Let's log the form field values and redirect after submission. We validate the credentials by checking for errors in the form data. If the email or password is missing, we set an error state. Otherwise, we use the Validate Credentials method to log the user in.
And let's just console. Well, I know what the form fields are called. So there's an email field, data.get the email. There's also going to be a password in there. Password. There's also going to be a Remember Me check box. And I'm going to call that one. So if the check box is submitted, its value by default is going to be on. So let's just go ahead and log those. And then let's just redirect back to the sign in route when the form is submitted.
Okay. So let's go ahead and pop into the browser. And oh, let's make sure we have our console open so we can see the submit as it comes through. So I'll just punch in my credentials here. And I'll click the remember check box and I'll say submit. Okay. So we see in our console log here, we see the email. Oh, how embarrassing. You can see my password. And the remember check box was checked. Cool. So you notice there was a post and then we redirected back to the sign in route. So then there was a subsequent get on the route.
What we're going to do now is we're just going to validate these credentials, right? If the email and the password were valid, then we need to log the user in. If they weren't, then we're going to redirect to the sign in page and maybe show an error or something. So what we need to do now is check these credentials and log the user in if they're valid. So let's do some validation here of the form data. So if there's no email, that's an error state. Otherwise, if there's no password, that is likewise another error state. Otherwise, let's say the user is where we have this method called Validate Credentials that we can give an email and a password to and that should give us the user.
6. Using Sessions and Redirecting
To persist login info across requests, we use a session that takes advantage of HTTP cookies. We set the user ID and redirect them to the homepage if logged in. Custom headers, like the set cookie header, are added using the commit session API. We can set a max age on the cookie to persist it for a week if the remember checkbox is clicked. Testing the login functionality, we are redirected back to the homepage, but the login link is still visible. Let's check if we can get the user on the homepage.
However, if the user is null, then that's another error error state. So we have a bunch of different error states, but this is the happy path. This means that the credentials were valid. So what do we do here? Well, we're going to use a session to persist the login info across requests. So sessions take advantage of HTTP cookies. So let's get the session and we're going to get it using the session. Remix provides a session storage API. And let's get it from the cookie header that comes out of the request. And so we've got the session objects.
So like we said, this is the happy path. Let's go ahead and set the user ID to the user.id. And let's just go ahead and redirect them back to the homepage if they're logged in. Except in this case, we're going to need to add some custom headers. In particular, the set cookie header. And we're going to use the commit session API to get the set cookie header back. So sessions come in via cookies. And then we tell the browser what the new cookie is with the set cookie header. So again, this is just web fundamental stuff. We're not doing anything new here. So we don't need that console anymore. Or we don't need this console log anymore.
One other thing that I wanted to remember to do is if they click the remember checkbox, I want to set a max age on this cookie. So let's say like a week, otherwise undefined. So if they click the remember checkbox, this cookie will persist for a week. Otherwise, it will just close whenever they close that tab. So let's go ahead and let's test it out. So let's go over here to our form. I'll go ahead and enter my password, we'll sign it in, and it worked. Looks like we got redirected back to the homepage, although we can't really tell that we're logged in because we still have this login link up in the corner here. So let's head back over to the homepage and let's see if we can get the user here.
7. Using Loader Function and Accessing Route Data
We're going to use a loader function to retrieve data for our route. If the session contains a user ID, we'll fetch the user data. Otherwise, the user is null. We can access this data in our component using route data. After refreshing the page, we can see the user object being passed to our component. We also have a form.
So we're going to use one more function here called the loader function. The loader function is kind of like your action function, except this one is for reads, right? The action is for posts and puts and things like that. This one is for gets, you can think about it kind of like your get server-side props if you are working in Next.js.
So we're gonna use the request headers. We're gonna grab that session, we're gonna grab the cookie, and we're just gonna return some data here that we are gonna use in our route. So the user is, if the session has that user ID key we can go ahead and get the user by ID. The session, we'll get the user ID out of the session. Otherwise the user is null.
And the way that we get this data down here in our component is we can use route data. So we'll give you a hook in react. This hero section expects a user prop. So we'll just go ahead and save that. We actually have a loader function now that's going to grab our user object, pass it through. So we are actually already logged in. So let's go ahead over here to our page and we'll just refresh. And we can see now, we have the user object we're passing it through to our, to our component here. And we're showing a different message on here. We've also got a form.
8. Implementing Logout and Error Handling
Let's take a look at the hero section component. If we have a user, we'll show a form for logging out. We'll add a sign out route and an action to destroy the session and redirect to the homepage. The destroy session method will generate a set cookie header to destroy the session. In the sign-in route, we'll handle error states and redirect based on success or error. We'll use the loader function in the next get to retrieve data.
Let's take a look at this component real quick. The hero section component. And you can see that in here, if we do have a user, we're going to show a form for logging out, right? Hello, username. Here's your button. And so it's just a standard, you know, post to the sign out route.
So let's go ahead and we'll add a sign out route here because right now we just don't have one. So add a sign out TSX. And this route isn't actually going to render anything. So the, the component for this route for now we'll just say is, is empty. What we're really interested in is we want an action in this route. And the actions job is to destroy the session and redirect us back to the homepage.
So let's go ahead and grab that session. Same thing that we've done now a few times. And we are going to return the action to that session. Our redirect back to the homepage, and this time the headers, the set cookie header, is going to be... We're going to destroy that session. Okay? So the destroy session method, like the commit session method, is responsible for generating a set cookie header. But in this case, instead of persisting the session, this is actually going to destroy it. So let's head back over here, we'll click our log out button. We're going to hit that action and we're going to get redirected back to the homepage, except this time no user, so user is null.
So let's head back over to our sign-in route and let's fill in some of these error states. Just like we can use the session to persist the user ID, we can also use the session to persist errors. So let's say if they don't give us an email address, we'll put a flask message, we'll put an error and say, please provide your email address. Likewise, if they don't give us a password, we'll say, please enter your password. Otherwise, if they give us some bad credentials, we can just say something like invalid user or invalid email and password. So in the case of success, we redirect back to the home page, right? But in the case of an error, we're just going to redirect back to the sign in page. So let's go ahead and we'll persist the cookie here. So that these flash messages can make it through. And then on the next get, what we can do is we can use that loader function that we just saw in the root route or in the index route. Let's get our loader in here.
9. Retrieving Session and Handling Errors
In our loader, we retrieve the session and check for an error message. If there is an error, we get the error from the session and send it as JSON data along with headers. We commit the session again to ensure flash messages persist across requests.
And this time in our loader, what we're gonna do is we're gonna get the session. And we are going to check for an error message. So if there was an error message, we'll do a session.get the error. And then the data for this route is really just going to be some JSON data. We'll send that error message. And then we'll also send through some headers. In this case we want to commit a session again. Because the case with these flash messages is that they only persist for one request. So if you go and you do a get on a key that was set with a flash, you're going to need to go ahead and commit that session again. Because the session contents actually changed.
10. Adding Error Handling and Server Rendering
Okay, so then we'll get it out in here. So error is use route data. And let's go ahead and add an error prop to our login form component. So that we can go ahead and we can show an error. So in our login form component, let's add the error prop. It's going to be an optional string. And let's say if we got an error, we'll just put it in here. We'll put it in some red text. We'll center it. I'll just put it right here.
OK, so let's go back over to the browser and let's test it out and see if it actually works. So if we go to log in with some junk credentials, we should see the error message here. Nice. So we submitted invalid credentials. We set the error in the session. We sent it back to the browser, the browser sent the cookie back with the session, we pulled out the error message, and we said, Hey, You can go and show it in the login form component. And then we re-rendered the page for the login form.
11. Introduction to Remix and Conclusion
There is so much more that I would love to tell you about Remix, but they only gave me a few minutes in this talk. We would love to tell you more about it over at Remix.run. We're still in a developer preview. We're hoping to launch a beta later this month. If you're interested, you can sign up at Remix.run. Or if you're interested in just following what we're doing, follow us on Twitter at Remix underscore run. If you're interested in following me personally, I am MJackson on Twitter and GitHub and everywhere else. So thanks again for watching the talk and we hope to see you around.
Q&A Session with Michael
Thank you, Michael. I really enjoyed your talk and was impressed by your coding skills. I have some questions for you, but first, let's see the results of the question you asked. 98% of the audience hasn't used Remix yet, but they're excited about it. Now, let's welcome Michael to the stage. He's had a lot of practice and prefers coding in his editor over using slides.
Thank you so much, Michael. I mean, all of you folks listening at home, go over to the Q&A chat and give Michael a massive round of applause for that amazing talk. I really enjoyed it. Also, I was really impressed by the coding that he did. I find it so hard to code when there's a camera or someone recording it. I'm definitely going to ask him for some tips for myself.
But we have so many questions coming in from you. Don't stop bringing in your questions. I'm going to shoot them over to Michael. But first, let's see the results of the question that Michael asked you. Michael asked if you use or have you ever used Remix and Drumroll. The results are that 98% of you haven't used it yet. Well, that is not a surprise considering it's not public just yet. But maybe you might be able to use it soon. I'm pretty sure lots of you are excited about it.
But I'm super excited to invite on the stage with me now, Michael, how are you doing, Michael? Hey, good to be here. Thank you for having me. Thank you so much. I really appreciate the detailed talk that you went in. I'm going to go watch it again. I can't wait because it was so nice to see you quickly build up that flow. Thank you, man. I've had a lot of practice. We've been doing the React training thing now for about five or six years. And as much as I would like to be somebody who does nice, pretty slides, I find that I'm just more comfortable just hacking out some code in my editor. So that's always kind of what I default to whenever I give a presentation.
Benefits and Use Cases of Remix Framework
The whole idea behind Remix is to allow the use of favorite web technologies or stacks. It's flexible and built on React Router, offering powerful concepts like nested routing. Remix focuses on web fundamentals, supporting the back button and data mutations via HTML forms. The framework has a great Discord server for answering questions. Remix shines in cases where CSS conflicts are avoided, thanks to unique class names for active routes. It aims to create quality HTML-driven documents, similar to PHP's approach.