Remix Fundamentals

Rate this content

Building modern web applications is riddled with complexity And that's only if you bother to deal with the problems

Tired of wiring up onSubmit to backend APIs and making sure your client-side cache stays up-to-date? Wouldn't it be cool to be able to use the global nature of CSS to your benefit, rather than find tools or conventions to avoid or work around it? And how would you like nested layouts with intelligent and performance optimized data management that just works™?

Remix solves some of these problems, and completely eliminates the rest. You don't even have to think about server cache management or global CSS namespace clashes. It's not that Remix has APIs to avoid these problems, they simply don't exist when you're using Remix. Oh, and you don't need that huge complex graphql client when you're using Remix. They've got you covered. Ready to build faster apps faster?

At the end of this workshop, you'll know how to:

- Create Remix Routes

- Style Remix applications

- Load data in Remix loaders

- Mutate data with forms and actions

136 min
04 Jul, 2022


Sign in or register to post your comment.

Video Summary and Transcription

Remix simplifies state management and data loading in web applications, combining modern web development with the simplicity of managing state in the backend. It supports nested routing and provides a convenient way to associate parent and child components based on URL segments. Remix handles data fetching, mutations, and error states automatically. It offers flexibility in accessing loader data and allows for multiple instances of loader data in a component. Remix also provides seamless handling of form updates, error handling, and redirects.

Available in Español

1. Introduction to Remix and Exercise 1

Short description:

Welcome everyone. We're going to talk about Remix and cover topics like nested routing, data loading, dynamic parameters, mutations, and progressive enhancement. In Exercise 1, we'll add a link to the post route in the Indy Stack. We'll go through the README and stop at loading data. Then, we'll create a post index route in our routes directory. TypeScript is recommended for building web applications with Remix, but it's not mandatory. Let's get started!

Welcome everyone. I am super excited to talk with you all about Remix. I think it's amazing and I think that we're all gonna have a great time learning about Remix. Let me get my screen prepared. And so this is the the workshop repo, and this is the workshop running locally. Let me bump that up just one here a little bit, scoot that over.

So here is our outline. We're gonna, we're not going to get through all of this stuff. We only have three hours, so we're going to work through some of this. I do want to give you some time with your hands on the keyboard, but because we're so limited on time I have a feeling that you're more interested in me or in getting through more content than spending as much time with your hands on the keyboard. That's my guess. That's the assumption I'm going to be operating under. So, because of that, I will give you a little bit of time on some of these as exercises, but most of the time you're gonna just be watching and asking questions and stuff in the Discord. And I think that will be the best use of the limited time that we have.

So we're going to talk today about nested routing, data loading, dynamic parameters, nested routing, mutations, progressive enhancement, and then there's homework, all sorts of things that we can do for the homework. Some of these things will just kind of breeze over and stuff. So I want to kind of give you an introduction to what Remix Even is and kind of the unique value proposition and everything. But I'd like to get things rolling quicker. And so I think we're going to do the first exercise and then we'll loop around and do a little is Remix Even. This is actually part of my strategy for learning. It's just kind of throwing you into the deep end. So we're going to start with Exercise 1 and you're not actually going to CD into this directory. So the way that this works is we have a special script called route runner. So this is called dev, that you can run, NodeDev and then you give it the path to the exercise that you want and it will run that exercise and what's cool about this is it will run it on a port that is associated to the exercise. So you could run all of the exercises and all of the finished versions of the exercises all at the exact and they will all work, which is kind of fun and fancy. So if you run NodeDev exercise zero one routing, and actually a quick tip, you can actually do just zero one and that will also work so you don't have to type the whole thing out, but then you pop open Locos 4001 and you're presented with this page that is the Indy stack. So Remix has this feature for project generation where you generate new projects and we call these stacks. It has a bunch of tools and stuff that come pre-installed and pre-configured for you. And the tutorial that we're going through is based on the Indy Stack. So that is where we're going to start is in exercise one with basically a pre-generated Indy Stack and your job for this exercise is simply to add a link right here, just below the big image here, to take you to the post route. We're going to add a blog to this site. So that's what we're going to do. That's your first exercise. You're going to go into exercise 01 routing, go to the README, and this will give you a starting place and a stopping place in the blog tutorial. So you'll start with your first route, and you will stop right when you get to loading data. So you won't continue through loading data, you'll just stop right here for this exercise. And so we're going to stick that in our routes index j.s. So this is our home page. That's where we want it to go. And we're just going to put it right there. It doesn't really matter where it is because we're not learning j.s.x. and proper structure or whatever. That doesn't really matter. So we're coming over here. We've got a link to blog post. And when I click on that link, we're going to get a four or four because even though we are actually going to that URL, we don't actually have anything there. And fun fact, because Remix is server rendered, you actually get a 404 from the server when going to that page, all the stuff that we've been doing on the client for the last six to eight years. When you have a route that is not not supported, you get the index HTML. So you get a 200. And the client just says, Oh, we can't find it. But Remix because of server rendered, can actually give a proper status code, which is really good for for SEO for the browser behaving the proper way all that stuff. So that's nice. But we want to have something there. And so in the next step here, we make a new route, and that is controlled by having a new route. And that is controlled by having a file at posts. And we want to have the index for our posts. So we'll create a post index route on in our routes directory. So let's make posts, and then index dot TSX. And Oh, by the way, I, I just assumed that we're all doing TypeScript. TypeScript is the way that you build web applications these days. And if you're not familiar with TypeScript, you'll be pleased to know that there's actually not a ton of TypeScript weirdness that you have to do with Remix. There's a little bit. But yeah, it is so so nice. So if you're not used to TypeScript, you can feel free to just leave the types off it will still work. You'll just get little red squiggles in some places. It's not a big deal. Okay, so we're just going to copy this because again, I don't think that you care to spend time writing jsx but the important thing here is that our module exports default, a function component. I guess it could be a class component. I don't know why I distinguished with function but you export a function or a component. And, and that is what's going to be rendered when the user lands on the post page.

2. Remix and React Router Integration

Short description:

So there it is posts. We have this link to the posts route and then the posts is a file under routes called, with a default export for what should be rendered when we're on that route. This would be with the convention that we currently have for Remix for the file structure, you can just have a posts.TSX. Remix uses React router remix came from the creators of React router. Remix is just using React router here. Remix is focused on web standards and modern web app UX. Remix takes the things that worked really well in the web 10 years ago, and then Remixes the things that work really well in the modern web. The thing that bothers people who have been involved in the web for a long time about the modern web is how hard it is to build a website these days. What Remix manages to do is, the thing that we liked about the older way of building websites is you didn't have to worry about managing state in the browser.

So there it is posts. And that that's everything that was the whole exercise was you literally just add a link using this link component that's coming from remix. And this is actually just being being re exported essentially from react router. There's a couple additional features but for the most part it's just being re exported. And in fact the additional features are being moved into React router proper.

So anyway, we have this link to the posts route and then the posts is a file under routes called, with a default export for what should be rendered when we're on that route. Could that posts folder just be a file post.TSX or does it have to be an index file? Great question. Yeah yeah. So we'll go ahead and do this now. This would be with the convention that we currently have for Remix for the file structure, you can just have a posts.TSX so we could put all this stuff in here instead and we'll get the same result and like, of course I could delete this or whatever but in the next couple of exercises, we're also going to have post slash slug with whatever the post name is and so we're going to need to have the post directory eventually. And so this is effectively the homepage for the post so the index page for the post and so that's what we just started with the index page. But it's not necessarily wrong to have this file because there could be like some sort of special nav or footer that you want for all your posts and that's what you would put in this. We'll talk about that a little bit more when we get into nested routing, doing the admin UI but just to give you a quick sample of what this is, this it would be the parent route of this index route and all the routes inside of this post directory and so parents need to say what where the children should be rendered and so the way that we do that is via the outlet component from remix run react. And so now I can say render my outlet and I'll wrap it in a div and say here's my footer stuff and my header stuff and then we come over here and I have my header stuff, the posts, this is the index route that's coming from our index right here, this right there and then our footer stuff. And so now every route inside of the post directory is gonna have the header stuff and wrapping whatever the child route is. And so that that absolutely is helpful and now this is controlling some of the layout for us. And then just saying where the the children should be rendered. But we don't really want to wrap anything. So we'll just do this and now what we're looking at is actually the default parent route. So if you don't have a parent route, this is the default basically is nothing. It's routing a structure that is something like React router six, which has outlet and it can some, put some routing inside them and have parents. Is it something like that?

Yes, that's exactly what it is. This remix uses React router remix came from the creators of React router. And so that's and React router is getting a lot of cool remix features very soon. Okay. Thank you. Yeah, in fact, fun fact, a couple months ago, is it months now maybe a couple weeks on our blog, we wrote this post called remixing React router, where we're taking a bunch of the really cool features from React router, and bringing them into, I'll just drop a link to that in the discord, bringing them into. or a bunch of the features from remix into React router. And actually, we could title this post differently, and instead say unreacting React router because we're basically taking all as we're taking all these features of remix and putting them into React router. We're just getting, we are kind of separating the core features of remix and React. And so what that's going to allow us to do is now React router is an adapter for remix router, and then we can make adapters for Vue and for Svelte and for angular and for preact and all of these things. So, the whole web is actually going to be able to take advantage of remix, regardless of what UI library you decide to use, which is pretty significant. So, the future version, and we have this on pre-release right now, we're thinking we may release this either this week or next week, where we'll have the remix router, and then just adapters for that router, which is cool. And community members are already building adapters for remix router, for Vue, Angular, Svelte and Preact. And, I think there may be another one, I can't remember which, but, so yeah, exciting times, and yes. So absolutely, remix is just using React router here.

I have a very basic question, maybe, like remix is running on the back end, not on the front end, like, if you would have a normal React application, it would run on the front end as a single page application, but remix is running on Node server or something, right?

Yes, yeah, but it is a SPA, a single page application when it hydrates, and so if you come over to your homepage, that was a, like, this is all server rendered, you look at the view source and you're seeing, like, all the HTML and everything in there, but when you navigate to blog posts, you're not getting a full page reload or anything, we're just loading in the JavaScript necessary for that page, and we'll do the same for the CSS and data and everything else as well. So, it is a client-side transition that's happening here, so you get like page transitions, focus management, all that stuff.

Now that we've done the first exercise, what I want to do is take a moment to talk about what is Remix, and this thing that you just toyed around with for a second, so Remix is focused on web standards and modern web app UX. You could say that we're remixing the old with the new, and so I'm going to, in Discord, I'm going to say raise your hand, raise your hand, here, I'll move this over so y'all can see what I'm doing. If you have been, or actually let's do how many years have you been in web dev. And so if you've been in for nine years, nine, there we go. If you've been in nine or more, oh whoops, that was supposed to be here. No stop chatting. We're just going to do emoji reactions. You're too fast. If you've been in nine or more, then do nine, otherwise do eight or seven or yeah, the number, you get it. So for me, I started web dev in officially 2014. So that gives me eight. So there we go, cool. So actually we've got quite a few of you who've been in for nine or more. Let's see. So for those of you who've been in for nine or more years, then you'll actually find this. Oh, there's a 10, oh man, I didn't know there was a 10. Whoops, yeah. You could do the 10 if you want to. So yeah, for those of you who have been in for a while, you'll find this to be very familiar and very comfortable because Remix takes the things that worked really well in the web 10 years ago, and then Remixes the things that work really well in the modern web in things these days. So the thing that bothers people who have been involved in the web for a long time about the modern web is how hard it is to build a website these days. There are so many things you have to do. You have your build tools, you have your linters and your formatters, and you've got your JSX, and you've got your JavaScript stuff. And now you've got to manage state in the UI and so many things that you have to do these days that you didn't have to do back then. And I would always convince myself and say, well, yeah, but our apps are way better than they used to be. We can do way more than we used to be able to do back then. And yes, that's true. The browser's way more capable. Good luck building Figma back in the late 90s or early thousands. Yeah, that's silly. So that's what I always convinced myself of. But what Remix manages to do is, the thing that we liked about the older way of building websites is you didn't have to worry about managing state in the browser. All state was basically just lived in the database and maybe you'd put a cache in somewhere in place to make things faster.

3. Simplifying State Management with Remix

Short description:

Remix simplifies state management in web applications by providing a mental model similar to the old days when state lived in the database. It combines the capabilities of modern web development with the simplicity of managing state in the backend. Remix eliminates the need for extensive UI state management code and allows for a full SPA experience. It also supports UI nested routing, providing a convenient way to associate parent and child components based on URL segments. Remix handles data loading and state management automatically, simplifying the development process and improving the user experience. Overall, Remix simplifies the mental model of web development and reduces the complexity of managing state in UI applications.

But for the most part, state lived in the database. And so when a user requested a page, they would just get whatever the latest was from the database. And when they make a post, we update the database and then we send them back new HTML that's based on whatever is in the database currently. And so it was just really a simple mental model because you never really worried about state. Maybe you put some stuff in a cookie or something so you manage stuff over, across pages that's local to a specific user. But for the most part, state management was really simple.

We bring in client-side JavaScript and now we have to manage state in the UI which is effectively just a cache at the backend and things get really complicated because we have a network IO or an IO boundary, which is our network. Things get really complicated. And so what Remix has managed to do is it gives us the mental model of the old thing where we don't think about state and the capabilities of the new thing where we have just, we like page transitions, focus management for accessibility, animated error messages, client-side error handling or as they type, show an error message. All of that stuff that becomes really, really difficult if you wanna combine some server-rendered thing with some JavaScript sprinkles. Remix manages to make it feel like it's not JavaScript sprinkles. It's a full SPA experience, but it's also giving us the same mental model of the olden times.

So just as a quick example of this, around the time where we threw away progressive enhancement as a software world, and we said, oh, when you're building a really complicated application, you have to just put the whole app in JavaScript, we created this thing called TodoMVC. TodoMVC was just to compare all of these different UI libraries in a single application to make it easier. So you've got just this application where I can type in whatever, I can check these off and all of this stuff, and it's all just stored in local storage. So what I did recently is I made a remixed version of this, and now, actually do I have a password for this? I can't remember, hold on a second. Yeah, try to guess that password. It is extremely long. Here we go. Sweet, so I implemented this. Oh, and by the way, it has user authentication. And it has actual persistence in a database. And so I could log into this on any device and have all the same to-dos. Not something that TodoMVC did. Also, it's keyboard accessible. Also a thing that TodoMVC was not. So, yeah, so I implemented this, and what's really, really cool about this is I can prove to you that for building a web application with Remix, you don't have to think about a UI state. Do you wanna know how I can prove that to you? Without even showing you the code, I don't even have to show you the code. Here's how I prove it. I say JavaScript is disabled. And so no JavaScript on the page. If I were managing my state in the UI, then I wouldn't be able to do this and have it still work, but it totally still works. So there is no code in my app for this implementation of this app that manages a UI state. So, like, just imagine in your mind all of the code that you write on your UI that manages state. It's probably like 30 to 50% of your code that you write is about state. You've got your Redux or you've got your MobX or you've got whatever other state library, if you're using context yourself, whatever you're doing. Probably about half of the code that you write is managing state. Remix makes it so you don't have to. And of course, like, users aren't disabling JavaScript in their browser, but this is just how I proved to you that you don't need to manage state in a Remix app because Remix is managing it for you. So when I do enable JavaScript, the whole thing works and it works better because we don't have to do full page refreshes. But again, I didn't have to change my code in any way. Remix is managing all of that stuff for me and it works really well. In fact, this app is really awesome because even if I'm on a really slow network, I can check these things off and it looks like they check off instantly. There's a lot more to this and I do have a talk where I go into this a lot more deeply, but this is called Optimistic UI and it's like outrageously awesome. It's super, super cool, where even though the request actually takes two seconds because I'm on a slow 3G simulated network, the UI updates instantly and Remix makes doing that sort of thing really, really easy. It's extremely resilient to poor network conditions. So that is one of like the coolest parts of Remix is that it simplifies your mental model. You can throw away all of the code that you have for managing state when you're building Remix apps. That's really cool.

In addition to that, we support UI nested routing and so as I think it was Ling was talking about earlier or no, I don't think it was. It was somebody else, I can't remember. Somebody was talking about earlier, we do have React Router V6 and so we have these outlet components that we were talking about and so for any given UI, you have this relationship between parent and children that are associated to different segments of the URL. And so for us, we had slash posts and so we made that slash post directory and that module and I showed how the parent post.tsx file could have some wrapper around it and then would render an outlet. And so that's how that association works is through that convention and through the outlet component. So the parent says, here's all the UI I want and if I have any children, this is where they go and this has enormous implications on your developer experience as you're developing your app as well as on the user experience as we load data and stuff is when you're working on this part of the UI that's just a single file that says, here's the UI that I render and here's the data that I need to do that rendering. And yeah, actually, you know what? I've got, I've actually implemented this app and we can look specifically at that route if we go under sales, invoices, invoice ID. And so this has the loader for loading the invoice details. This is literally all the data that that... Actually, I implemented more because I have also like deposits and stuff like that but it has all of this data that is needed by the UI. It's all just right here running on the server only on the server. So you can like talk directly to your database and stuff like that. That's what this get invoice details thing is doing. And then the UI down here just says, hey, get my data for what I need to render and it will render that out in the JSX. And so you don't worry about what your loading states look like in this part of the world now. Of course sometimes you're loading, it is going to take some time to load because you do have a network and sometimes it's slow. And so we do have really great APIs for pending states. But it drastically simplifies this because you're not worrying about keeping this data up to date. Remix will keep this data up to date for you automatically, handling the entire network chasm for you. And there's nothing in here that says, oh, well, my parent route needs this data, so I'm also going to load a bunch of data from my parent route. This is literally just what this sub-route needs to render what it's going to render and everything else is managed by its parents.

4. Nested Routing and Data Rendering

Short description:

The parent component is responsible for rendering its data, such as the invoice list items, overdue amount, and due soon amount. The sales route and the parent layout route handle rendering links and the outlet. With Remix, teams working on different parts of an app, such as the header and footer, don't need to communicate about data rendering. By loading data next to the component being rendered, Remix provides a snappy user experience without bouncy loading states. This eliminates the need for excessive spinners and improves the overall user experience.

So the parent for this one will be the invoices.tsx and it renders all the stuff that it wants plus the outlet, let's find that, yep, right here. There's the outlet. This is where the invoice list will be, I guess. And so, the parent is just responsible for its data. Here's that right there. The parent just needs the invoice list items, the overdue amount and the due soon amount. So here we can see that right here. The overdue amount, due soon amount and the list. And then on the right side of the list, we just render where our child is. Same thing with the sales route. So if we go to the sales tsx, this is the parent route here. It's responsible for rendering all these links and then it renders the outlet and that's all. It just renders the links and then the outlet. And then the parent layout route here is this apt-tsx and it's rendering all of the NavLinks on the left. Actually, we have our spinner for if there's ever a transition going on. I noticed Stefan's in here and Stefan actually wrote this library, Spindlay. So shout outs to you, Stefan, that library is awesome. And we render our outlet right there. So it's the layout route for our app is responsible for this left Nav and then it renders the outlet and says, if I have any children, this is where they go. So this is awesome. So imagine that you're at a company and you've got like three teams working on one app or even just like two engineers working on one app like it scales for small apps to really large apps. And so one of those teams we'll say is responsible for the overall layout of your site. They do the header and they do the footer and then they don't care about anything else. Without UI layout routing, those teams have to communicate about the data that they need to render. Because if every single page in that app needs to render the header and the footer and you've got a team that's responsible for that. Anytime that team needs to update the header and the footer, they need to communicate to all the teams and say hey, you need to pass me these extra props or whatever. And so what typically happens is we say, let's just manage like loading the data that we need on the client and that way we don't have to communicate this to anybody. We'll just load it like next to the component that we're rendering and so then they don't have to pass us any props. This leads to a poor user experience because you end up with this kind of bouncy loading state rather than, actually I've got fake books right here. And if I log in and we go to the sales page, we'll go to invoices. Yeah, all my customers are overdue, they're in trouble. But if I go to this page, like you'll notice that it renders instantly and that's exactly what we're demonstrating here is that it boom, it's rendered. It looks awesome. Rather than having this bouncy experience that we have like everywhere. Like watch this, I go to and we've got a bunch of spinners all over the place. What is this hot garbage? No offense to because like almost all sites are doing this. We got stuff bouncing, oh, actually I hide this because I don't like these pictures but like sometimes they have like really stupid things going on over here. But like you got spinners all over the place, all over the web. Why are we doing this to our users? There's a reason, there's a good reason and we don't have to do it anymore because Remix exists. So yeah, rather than having the bouncy experience on the left, we can have this snappy experience on the right, give our users a much better user experience and we can have an awesome developer experience because the team that's responsible for the header and footer can just be responsible for those things and they don't care about the child and the child doesn't care about the parent. This is, maybe that metaphor is not good because parents should care about their children and children should care about their parents. But in the world of Nested routing, they don't have to care and I think that is awesome.

5. Remix Capabilities and Data Management

Short description:

Remix can do a lot of awesome things. It handles bundling, compiling, and data fetching. It prefetches data based on user navigation, making it fast. Data mutations are simple with a form and action in the same file. Remix manages data, form resubmissions, race conditions, and error states.

Okay, so we also like, there's a lot of really awesome things that Remix can do because it's your bundler and your compiler and your data fetching library like prefetching data based on where the user is going to be and so the navigation is really, really fast. And then data mutations is really good and we'll look into that as well, basically it's just this form, that's it. You have a form and then on the server you have this action and those two things are literally in the same file. So here on the invoice ID, we have this action which can, will be called whenever we want to create a deposit on an invoice. And this only runs on the server and your associated form is just right here and it's amazing, it is so, so, so, so cool. So we'll get into that a little bit later but Remix manages keeping your data up to date, it manages form resubmissions, it manages race conditions, all of the things that we typically skip over when we're building this ourselves because we're just too busy trying to get the things shipped in the first place. And Remix has taken care of all of that for you. Also error states are handled and it's awesome but we don't have time to go too deep into that.


Scalability, WebSockets, and Data Sharing in Remix

Short description:

My team is currently using Next.js and we're discussing how to render and avoid server costs. Remix can achieve similar performance characteristics as SSG by setting up a CDN and using cache headers. It also provides levers to handle different use cases and needs. Remix supports WebSockets and server-side events, making it suitable for real-time multi-user scenarios. The port number corresponds to the exercise, and data can be shared between routes using the use matches hook.

I have a question about scalability and server, I was just wondering, my team were doing a project with the Next.js at the moment and we are having big discussions about how we render to avoid server costs and so on. How does Remix scale in this situation? How they fare?

Yeah, that's a great question. So in a Next scenario, your conversations are probably like, well, let's SSG this thing so that we don't have to hit our server. The interesting thing about that approach is that unless everything on the page is totally static, which I'm guessing it's not, like you will have loading spinners to have data, you're still hitting your servers. And so to go get that data. Now, maybe you're using a third party, so you're not actually hitting your servers, you're hitting somebody else's. And in that case, yeah, you do get a benefit. But I will say that you can actually do the same thing in Remix. We do not support SSG, but SSG is just a custom implementation of a CDN with cache headers. And so if you wanted to have that, the same sort of idea as SSG using Remix, which is not, does not support SSG then you would just set up a CDN like Cloudflare or Fastly or whatever. You'd set up that, and then you'd have your server send back S max H headers, directives on your cache header. And then the CDN will next time somebody comes around the CDN say, Oh, that's cached, let me send the cache version. So it's not gonna hit your server. So you get the same performance characteristics with SSG without having to do SSG. The really nice benefit of this is that if you go with SSG from the beginning and then later decide, Oh, we need some more, like we want a server render this actually. You have to completely rearchitect your solution. If you start with server rendering and just use cache headers, and then you realize, Oh, we need to server render more. We can't just have it be in a shared cache. Then you just turn off the cache headers and boom, now you're server rendering. But as far as scalability, if that's a concern, then just use cache headers. And then yeah, keep in mind that you're not solving scalability issues if you do have to make network requests to get user data, because like, you're still hitting your server in that case. So I would suggest cache headers. And if, like, if you don't wanna have a bunch of spinners all over the place, then I would suggest caching on the server with something like Redis. And yeah, what's really cool about Remix is that instead of giving you lots of different approaches to a problem, we give you levers to a problem, to handling different use cases and needs. And so, rather than having to take this approach for this use case and this approach for this use case, you actually just take a single approach and then use the lever to kind of switch on a per-route basis the sorts of things that you need. And actually, a really great talk that Ryan gave recently at Reactathon, Reactathon, see videos. Let's see what, yep, there it is. Let me grab that URL, and I'll just stick this in discord and somebody can put it on the note-taking thing if they want. But here he talks about those levers a little bit and some of the really, really cool things that are coming pretty soon too.

I have sort of a question just since we're also on the topic of Ryan Florence. I think I saw a tweet by him a little while back where he was talking about fielding the question how does Remix handle websockets and how oftentimes that is just a sort of question about does it handle like real-time multi-user scenarios? And the answer he proposes there is like service and events and event source, which I think is very interesting and compelling when the sort of communication you want to have real-time involves a form submission, but like I work on a team where we're building an app that is sort of, I guess, as real-time as something like Figma, where you want to be able to see the other users' like mouse positions or have them take actions that don't involve form submissions and then propagate that state through the app to like multiple clients. Is that something that the sort of like networking layer of Remix is considering trying to solve for or is that too deep and specific of a question for right now?

No, no, no, that's a great question. First, I'll say that Remix supports WebSockets just as well as any other framework you could use, so there's nothing that's better suited for WebSockets that I'm aware of. So if that's the technology choice that you wanna make, that's totally fine. But I don't believe that server-side events requires a form submission. So like you can set up a server-side event request without having a form submission at all. So you could totally do Figma, mouse position, all of that stuff with server-side events. And the benefit of using server-side events is that it's just a request, so there are a lot of really cool things that come with that. And it's supported right now out of the box with Remix. You can have a resource route that sends back a response that establishes that event stream. And yeah, and you could totally build that sort of UI with server-side events. And I think that's an underutilized technology because we got WebSocket's and we thought, okay, this is awesome. And it really is quite cool. Although hard to scale, that's why we have stuff like Pusher. But yeah, so I would say maybe look into server-side events again if you want, but if you're happy with WebSockets, that also was great. I don't, maybe eventually we'll have something, some built-in support for WebSockets, but we're more interested in thinking about use cases. And I think that the use case you described is actually pretty well-supported by server-side events. But I don't have too much time to go any deeper into that though.

So just keep in mind that your port number, whoops, this is the wrong app. The port number that you are looking at when you're looking at the app should correspond to the exercise that we're on. So we're on 4001, it's exercise one. So now you want to start on exercise two, so now the port number should be 4002. So that's where you should be at. We have the blog post link, we get the post right here. And for exercise two, we're on data loading. If you go to the read me, it'll show you start with loading data and then stop when you get to pulling from a data source, which popped up in my other browser. Yeah, so stop right here when you finish with the data loading section here. So I had a couple of questions that I'll just answer here really quick and then we can get into actually doing this part of the exercise. So Felix in the Zoom chat asked, how do you share data between routes? So if you need to have data shared between routes, we have this thing called use matches that allows you to share that data or to access data on other routes. When you're talking about between routes, it's just like parent to child or child to parent. So if the parent needs access to something that the child has for like a breadcrumbs UI or something, that's what use matches is for. If the child needs something that the parent has, then you also use matches. And we actually do that as built in part of the Indy stack in our utils here for getting the user. So we have this use matches data hook that I wrote that just basically uses the matches, finds the routes by the route ID. In this case, the user is in the route route and so we just get the route route data. And then we get the user off of that. And so, yeah, you can share that up and down the chain and it's pretty straightforward. You don't often have yourself having to do this sort of thing for more than just like user data but sometimes you do. And so that's why we have support for that in the first place. So yeah, hopefully that answers that question.

Data Storage and Loader Function

Short description:

Remix simplifies data management for UI, but storage is outside its scope. You can use Remix to fetch data from a server using a loader. Remix can also handle resource routes for REST or GraphQL APIs. Building a web and mobile app in Remix with co-location is a possibility. The loader function runs on the server, allowing for secure operations and heavy libraries. Console logs in the loader only show up in the terminal, while logs in the component appear in both the terminal and the browser.

And I think that was all on there. There was also a question about where the data is stored. So in our app, we're using Postgres or SQLite, but yeah, like data storage is outside of the scope of where Remix is currently. Maybe one day we'll have like an ORM built in and a database built in or something, but that's pretty unlikely. Your data can be on a third party or it can be in your own database or wherever you want it to be. But yeah, Remix just makes it really easy to get data in and out for your UI.

Is Remix right tool for a widget? You wouldn't use Remix for the widget itself because Remix is about like the whole application and routing and stuff. A widget doesn't really do any of that. But yeah, you can totally, I think Nick actually answered this. You can publish a React component, use that anywhere. That's probably how I would do that. Yeah, I think that's pretty, oh yeah, as far as like how Remix fetches data from the server. So there's like a special URL scheme that's an implementation detail that you shouldn't utilize yourself for making these requests to get this data. So we've got this loader. Remix makes a special fetch request to get that data during client transitions. But you could totally make what we call resource routes, which are routes that don't have a default export. If it doesn't have a default export and then it's a resource route, you can hit that URL directly. And so you could totally take your REST API or whatever your GraphQL API, build it in Remix as resource routes, and then your mobile app could use that. And we're actually thinking it should be possible in the future to build a web and a mobile app, like a React Native app, in Remix and have this co-location just like we do for the web side. I think that that should be possible and that would be pretty legit. So we're not actively working on that, but that is something that we've thought about.

Okay. Yeah, question about why does console log inside the loader only show up in the terminal, whereas in the component, it shows up in both the terminal and the UI or the browser. So the reason is that your loader function and that loads data only runs on the server. This is great because it means that you can use private keys. You can talk to a database. You can do like all of the things that are really annoying. You can use really heavy libraries and not have to ship those to the browser. All sorts of things that you can do in this loader. And then remix is just the one making sure that we can go between the loader and the browser and get that data across that network chasm. And so that's why you're seeing the console logs in here only show up in the terminal because that's all that, that's the only place that it's running. For your component, you're seeing the console log in the terminal because that is running on the server for the server render. And then you see it in the browser because it's hydrating. So that's what's going on there.

Data Loading and Response Handling

Short description:

We're going to handle data loading for our posts. We'll use a special function called json from Remix Run node to create a new response with json or json-stringified format, headers application json, and status 200. This function simplifies the process of creating a json response. Remix normalizes the request response on any platform to the Web Fetch API, allowing you to work with the web platform without worrying about deployment specifics.

Cool, great. So let's work through this. We've got, close all this stuff and come back over here to our readme. So we're all on the same page. That is different browser window. There we go.

Okay, so we're gonna handle data loading. We need a loader for our posts because we're just gonna have a bunch of posts in here. So I'm gonna grab this and bring it over to our posts index so we can list an index of all of our posts. Let me get rid of that terminal.

So this json is a special function that we're gonna get from Remix Run node. And it's actually not all that special. Really what it is is it's a new response and this is json or json-stringified and then headers application json. That's basically all, oh, and also status 200. Not 2000, just 200. This is complaining because, oh, right. This needs to be, ah, content type. There we go. That's all the json is. It's useful and I don't wanna have to type that every time, you're basically always using json in your loader and so that's why we have the function but that's all it's doing. It's making a new response. Now, as I mentioned, this only runs on the server side and so in environments where the server doesn't support request response, Remix is converting the whatever the request is for your serverless thing or your Express thing or whatever you're using, it converts that request into a Web Fetch request and it polyfills that in those environments where Web Fetch request isn't supported and then the response that you send, it converts that Web Fetch response into whatever your serverless or your Express or whatever needs for its response and so that's one really cool thing about Remix is we normalize the request response on whatever platform you're running on to the Web Fetch API. So you're always working with the web platform and you don't have to worry about the specifics of whatever deployment target you're targeting.

Fetching and Rendering Data

Short description:

We send our JSON with the post and access it using the use loader data hook. The posts are retrieved by destructuring the data. The server render and client-side transition both work seamlessly, with logs appearing in the appropriate environments. During a client-side transition, there are no logs in the terminal as the component is already hydrated on the client side.

Cool, so we're sending our JSON with this post and to get access to that JSON, we're gonna say use loader data, whoops, use loader data, there we go. To get our posts, we'll just destructure that and cl posts. Cool, so if I console log that and I come over here and refresh then we're gonna see our posts right there. We've got our posts, let me pump that up a little bit. Cool, so that all came from the server. This, as was mentioned, if we console log here on the server and save that, if we look in here, we actually see here on the server and then we see our posts, that's for the server render. And then if we come over here, we only see the posts. So that's handy-dandy little feature of Remix handling that transition. And also this totally works for a client-side transition as well. So we do the server render over here and we click on this link. We're still gonna get that log that shows the posts, we're still gonna get this log. But you'll notice we do not get... Oh here actually, let's do this. Server render of posts. Let's do that. Okay, so if I come over here and now we clear all that out. So I do a transition to the blog post. We see, oh ha ha server render of posts. That wasn't that component, that's wrong. But you will not see the server render of posts in the terminal because this is a client transition. So we did not need to run the default export. That's already been hydrated. All that is now on the client so we don't need to re-server render this or anything like that. So there's no logs when you're doing that client-side transition. Hopefully that makes sense.

Loader Data and Use Loader Data

Short description:

We define the JSON that should come back from the loader as a convention. However, using a generic for use loader data is not recommended. TypeScript cannot verify that the data received matches the generic passed in. This is a TypeScript-specific issue. We are working on improving this in the future. For now, it's important not to unknowingly deceive yourself. Remix ensures that the loader data for a route is loaded before transitioning to that page. There is no asynchronous loading happening at the point of using use loader data. You don't need to worry about a loading state in this case. The loader is not a special keyword, but it is necessary for use loader data to work. When the user transitions, Remix recognizes the loader for that route and makes a fetch request. Remix handles the fetch request and passes the response to the UI.

Okay, cool so we've got our post. From here it's just JSX nonsense so we're not gonna bother with that. I'm gonna copy paste this and stick that right there. Let's bring in the link from RemixRunReact. Save that and now we've got all our listing of our posts. And then we just do some TypeScript stuff. I don't think that you'd care to learn about how to type all this stuff correctly or at least you don't need me to type the typings. So what we do here is JSON is a generic where you can pass loader data. What, okay this is a mistake. I do need to talk about this, gosh dang it. If somebody wants to make a PR to the tutorial please do because this is wrong. You should not use the generic here and I'll explain why here in a sec. But yeah, so we're just defining, hey this is the JSON that should come back from this loader. This is just a convention that it doesn't actually matter if that's, this is the way that you do it. You could inline it there or whatever. You could call it whatever you want. But I call it loader data and it's an array of posts that are slug and title. And then on the client side or on the UI side we have this loader data. We're using this generic for use loader data. You should not do that. And the reason that you should not do that is because this is exactly the same as this. Because there's no way for use loader data to actually know that the data that you're getting is the generic that you passed in. So this is not a remake specific thing. This is a TypeScript specific thing. So if you, here's a quick tip for you. So, if I get thing, and then here's my generic, and then return i as generic, never do this. Almost never do this, unless you absolutely know for a fact that this is actually the thing that you think it is. Because I can now say foo equals get thing as a number. And now foo is, is that a number? Oh my gosh, it's a number. But it's not actually, and I don't know that I've just lied to myself. Because when you make a generic that says that here's the thing that should be returned, you think that the function is handling that in some way. But what it's actually doing is it's just lying to you. It's saying whatever it is, whatever it wants to say. So, that's why we decided to get rid of the, or to deprecate the generic, because you're lying to yourself. We don't actually have any way of knowing, for certain that there wasn't some weird thing that happened across the network to blow up your loader data or add properties or who knows what it did. And so, that's just the nature of a network thing. We have the same problem with an IO boundary of the file system. Somebody saves a file, and then somebody reads the file. You cannot assume that when you read the file, it's the same thing that was saved, because something else could've changed that file between those two things. Yeah, go ahead. You could, however, pass the function itself, like the load data as an argument to a type, and then you could deduce that from the type of if you- Yeah, so we're actually working on improving this, and eventually, you will be able to do this. Yeah, exactly. That will eventually be the way that we do this. Exactly, this is what I was- You can't do this today because the loader returns a response, and you cannot deduce that right now, but yeah, eventually, that's what it will be, and it will be a lot better, but for right now, don't lie to yourself unwittingly. You should lie to yourself wittingly. Does that make sense? If you're gonna lie to yourself, you may as well know it. So anyway, I wish that I had updated the blog post tutorial so that I didn't have to spend the last five minutes explaining why it was wrong. So there you go. And if it didn't make sense or if you got lost in all that, don't worry about it because it was wrong anyway. So we're good. Okay, that is it for loading data. Any questions about loading data in Remix? We're gonna take a break after this one, so if your brain is starting to feel fried, don't worry, a break is gonna come save you. So- I'm just wondering is use loader data automatically using the loader function then? Yeah, so the way that it works is Remix won't actually transition to this page until the loader data for this route has been loaded. And so when we say use loader data, Remix already has the data. And so it just finds the data for the route that is asking for it and it will pass that to you. And so there's nothing asynchronous going on at this point. All the asynchrony has happened before we got here. And that's why you don't need to worry about a loading state right here. Now, we do have APIs for loading states because sometimes the network is slow, but you don't have to worry about it here. Any other questions? Hi. Is the loader a special keyword we have to use for use loader data to work? Sorry, I don't understand your question. Could you repeat it? So the loader variables unify about. Is that a special keyword we have to use for use loader data hook to work? But it's not important anyway. So we export the loader. And Remix is gonna, Remix will know, oh, there's a loader for this. And so when the user transitions, it'll say, oh, there's a loader for that route. Let me make a fetch request. And then when that fetch request is made, Remix is the one that handles it. And it says, oh, you want to call this loader. So let me call that loader, get the response back, send the response to the UI.

Loader Data and State Management

Short description:

Remix provides flexibility in accessing loader data and allows for multiple instances of loader data in a component. The useFetcher hook can be used to load data on the client only. Parent loader data cannot be accessed in the loader of the child, but it can be accessed in the component. Future versions of Remix may introduce an API for accessing parent loader data. State management in Remix covers everything and is easier to use than Redux. However, Remix state management focuses on persisted state, so local UI state that is not persisted will not be managed by Remix.

And then when that's done, Remix takes that response, sticks it in it's data that it has. And then when this is rendered, it says, oh, you want the data, here it is. So if I change the name to something else, like loader one, it is still work? No, it will not. This is a convention. It has to be loader. It has to be loader. Yep. It's basically happen in the backend somehow. Sorry, what was that? So it's like the Remix, it's only detect the loader keyword, in this case. You kind of have a different name for that. Right. And can you have two views loader data in one component or you have only one? Yeah, you could use it twice and it would be the same data. And the benefit of that would be if I've got a sub component and I need the loader data, you can get it there and not have to pass props all over the place. So yeah, there are totally situations where you do that. Can I have two different loaders or can I have one loop so I can, if I have request to resources from two different things, so I need to do it in one loader or can I have two different loaders? So there are different use cases that we're talking about here, but the most obvious one would be, here's my first thing and second thing is wait, promise.all, and then get first thing and get second thing. And so now you've got both of those things, you send them both back. But if one is really slow and you don't wanna make the page wait, you'd rather send a loading spinner, we've got a really awesome API for that coming up soon called the Deferred API, it's gonna blow your mind. But even today, if that's what you wanted, you could use a hook called useFetcher and load it on the client only. And yeah, so that is possible today, but we've got something even better coming, that's a little bit deeper than a Remix Fundamentals workshop though. So that's as far as I'm gonna go with that. Thank you. Yeah. I have a small question maybe, if you still have time. Yeah and then Ling, see your hand is raised as well. Yeah, go Ling first, I didn't raise my hand. Okay, that's all right. Hey, thank you. So they're probably just a very small question. So there are probably other API functions other than loader available, right? Yeah, yeah, you mean other exports on this module? Yeah, there are. If you go to conventions and then go to route module API, you got all of those right here. Can you- I'll paste that in the Zoom. Thank you. And the discord. All right. Okay, go ahead. Is it Gunther? Yes. Yeah, say that, for example, you have a URL with two IDs in it and you have the lowest child. Can you access the ID from the parent? Yes. For example? Yeah, so you get this params object and that will say, like slug and then you can get like whatever the parent ID. So like if we're selling cars or something and say params.make and params.model. So you have slash slash make slash model, you could access them all. And can you access the loader data from the parent? No, you cannot. So, the reason for that is, when you have multiple routes, we want to run all of those in parallel. We don't want to run one after the other. Otherwise, it'll be really slow. And so, no, you can't access parent loader data in the loader of the child. You can in the component, just not in the loader, cause we run those all in parallel. And also. So, you have to re-fetch it, basically, if you needed the parent data? Yes. Okay, perfect. Now, keep in mind that this runs on the server. And so if you have to hit the database to get the model and the parents, and then you hit the database to get the model in the loader, or in the child, those are running on the server. So, that's probably fine. But if it's not, then you could implement a mini caching layer on your server to say, oh, I'm already requesting this, just hang on while I go get it for the other one. So, that is not ridiculously complicated. But, we're also eventually going to create this special API that'll probably look something like this before loader. And so this will be something that just returns some data that you want to have. And this will run before any of the loaders run. So you really don't want to use this because it automatically makes all of your loaders take as long as this takes before they even start running. And so like, this is only for those things that are really critical for all of the children. But yeah, there will probably be an API like that in the future. Perfect, and how do you fetch the parent data with the use loader data? Or is that not part of this? Yeah, so if you wanted to access the parent data, you'd say use matches, this will get you all of the matched routes and your parent data will be in that. Is there anything regarding state management that only work using Redux approach or does Remix cover everything? There's literally nothing that you can do with Redux that you can't do with Remix and Remix makes it easier. So the one thing that I, caveat to that, is that Remix state management is all about persisted state. So if you have UI state that only shows up in the UI and you hit refresh and it's gone, stuff like whether a modal is open or menu item is selected or something like that. Then yeah, that stuff won't be managed by Remix but most of those things are like component level things so you wouldn't use Redux for those or you shouldn't. It's a really great way to make a very slow react app. But yeah, so those are all just like local state things. Sometimes there are things that you're like, I kind of need to share this across the app and it's not persisted so I'm not gonna put it in Remix. Yeah, there aren't a lot of situations like that but there are some I'm sure.

Using Remix and Dynamic Parameters

Short description:

The latest version of React router supports using Remix. Redux may still be useful for highly pluggable apps. The JSON method in the loader has implications for stringifying objects. Testing Remix apps is important, and the stacks come pre-configured with testing tools. Applying structure checks or schema validation to loaders is not allowed due to the build process. Dynamic parameters in Remix routes are straightforward to implement.

And you can still use Remix for that because the latest version of React router supports this, hey where is my... Oh right, I got rid of that. Let's go to notes. So the latest version of React router has this feature on the outlet called context. And so you can put whatever value you want in there and then the child over here can say, where's my, yep. Can say use outlet context to get whatever was passed in the parent context. And so if you wanted to do some UI state that shared across the routes and stuff like that, then you don't even have to make your own context provider, you can just use the context API from outlet. But if you don't wanna do that, like you do wanna make your own context provider, that's also totally fine. I can't imagine. So actually there is one situation where Redux might be useful still, in my mind, and that is if you're building an app that is a very pluggable, so like other, like your users can write their own plugins for your app, and they need to be able to plug into when different things happen. I'm thinking of stuff like Hyperterm where you can have plugins and when there's some action that's dispatched, the plugin wants to be able to do something or whatever. That actually seems like a reasonable use case for something like Redux, but that's like point 0.00001% of use cases. So I don't really see many situations where Redux is very useful.

pepf, so the JSON method in the loader has some implications, nothing new, it's all web standards, that's good to know. Stringifies, yeah, stringifies objects and like JavaScript objects like date and regex causing data to be lost. Yes, that is exactly right. And that's actually why this is not awesome because if I were to say, hey, this is my date and it's a date object, then my loader is fine with that. Like I can say date, new date. Like that works fine. But over here, if I were to try and say post date dot to local string, whatever, that's gonna blow up my app because it was serialized across the network. So that's why we don't want you using the generic as it works today, we don't, like this is technically lying to yourself because it is because this was stringified, it happened over the network. You'd have the same problem in any case, like regardless of whether you're using Remix, you are going to stringify dates and other objects like that. And so that's why when we have the support for type of loader, what that's going to do is it's going to say anything that was in the loader data or like in what you returned from JSON, anything you put in there will be turned into a JSON stringified version of that thing as far as the types are concerned. So this will throw a type error in that future. And so, yeah, the way that I solve this is if we go to the FakeBooks repo again, cause we do display dates in various places. Is I, where is it? In my loader. I actually formatted on the server and just send the formatted value. You could also serialize it or like, yeah, serialize and deserialize it on the client if you wanted to, however you want to accomplish that, but yeah, you do need to think about the fact that everything you pass to JSON is gonna be JSON stringified. So just keep that in mind.

Oh yeah, talking about testing a Remix app. You don't need to test a Remix app because you write perfect code when you write a Remix app. You never have bugs. It just don't exist. I'm just joking. That's a total joke. Yeah, we probably won't have time to talk about testing. But the stacks are all pre-configured with testing tools and stuff anyway. So yeah, if you start up with a stack, you'll have all the testing stuff configured. Okay, yeah, Chris, and then we're gonna move on. Just a quick question, in general. I just want to know if it's possible to have the loader that you have in all your components, if it's possible to rep it in a general way so you could apply something like structure check or like some schema validation, whatever data came in that I can prove that this is definitely valid and I can trust the structure or show a general error message. Would that be possible at all? Yeah, so what you're asking for is a way to say like validate thing and just do like a higher order function sort of thing. This is not allowed because we're taking this one file, we're creating two different builds out of it and one for the server and one for the client. And for us to be able to get rid of the server only code, you can't have any side effects in your module. Otherwise we can't be sure that we're doing the right thing. And so higher order functions are not allowed. You could totally make your own my own JSON and do that same thing there. That's probably how I'd do that. Okay, I was just wondering because you mentioned before this before loader, I expected there would be something like after loader, maybe this would be good. We do have in the entry server file, this is handling requests for the initial page. And so you're responsible for making the markup and all of that stuff, which is actually really nice. We also have a function handle data request. I think that was what it's called. But this is the last part of your code that gets called before the data response comes back. And yeah, and so that you could do that sort of thing there. Great, let's get this show going. So if we look at our outline, we've got nest routing, data loading. I'm gonna just show you dynamic parameters really quick. It's pretty straightforward and it will save us a little time. So if we look at routes post, oh, that's the exercise. Let's look at the final version of that. Routes posts, then you'll see, we have this dollar slug thing dot TSX. That's how you do a parameter in URL. So you go to post slash anything and the dollar slug will just be whatever value that was in the URL. And then you have access to that via params. And the one interesting thing about this is that, hold on, did I, I kind of skipped over a couple of things. Yeah, hmm, should we? Yeah, no, we don't have time. And we're over halfway done already. So that's all there is to dynamic params. You give it a dollar, whatever you want to call the param and then in your code, you use the params in the loader.

Params, Database, and Next Exercise

Short description:

To get access to params in your component, use use params from RemixRun React. TypeScript cannot know if the slug will be defined, so we use the invariant function to check and provide a specific error message. We also moved the database connection to a separate file using Prisma to access data in the SQLite database. We retrieve posts using Prisma's find many and find unique functions. If the post doesn't exist, we throw an error. If it does, we compile the Markdown into HTML using Marked and render it. The next exercise covers nested routing and mutations, which are the most important topics. Make sure you're in the right directory and run node dev exercise 04 on port 4004. We won't automate the params check in loaders because it would be difficult to teach TypeScript our convention. We're adding an admin section to the app, and there will be a link to it.

To get access to it in your component, you use use params from RemixRun React. Which is the same thing as from React Writer. So if you're familiar with that, that's that.

So that's it for params. Now, couple of notes. For one, TypeScript has no way of knowing whether this slug is going to be defined. And so that's why we're using this invariant thing. This invariant thing, if you're not familiar with it, is basically the same thing as this. And what this does for us is, first off, it gives us a nicer error message than cannot call to uppercase of undefined or whatever. So we can have a more specific error message because there's no way for us to 100% guarantee that this is going to be what we think it is because we could actually change this, like typo this to slugs, and now the parameter name is changed. So because of that, it's a good idea to do this check. And when you do this check, TypeScript also will consider this to just be a string, whereas before, it could be a string or undefined. So that's what the invariant thing is all about. And you'll find that I use invariant quite a bit just to say, hey, I know that it, or I think that it is of this type. Let's just like verify that, and if it's not, then we'll get a nice error message for that. So that's all that was about.

The other thing that changed here is we skipped over the part where we're connecting our posts to an actual database. And so here, we're using Prisma for accessing data in the SQLite database. And I moved everything over to this other file because when we're talking to a database, I like putting that stuff in a separate file most of the time. And just like, here's my model to communicate with the database. And so, we move this into the models directory. And yeah, to get posts, we say Prisma post find many, and to get a specific one, we say find unique where the slug is whatever you're looking for. And so on that slug page, we can say, get posts, params.slug. And if that doesn't exist, then we throw an error and say, hey, that doesn't exist. We have better error handling in a 404 management that we'll talk about later if we have time. But for now, we're just gonna throw an error if the post doesn't exist. And, but if it does, then we're going to use Marked to compile the Markdown into HTML. And we send that across the wire, and then we render out that HTML for the post. And so, by the time we finish that exercise, if we go to node-dev, yeah, vinyl03, what happened? Oh. There we go. Ah, dev, vinyl03, there we go. So when that's all done, oh, it's on the wrong page again, then we end up with a thing that actually works. My first post, this is my first post, isn't it great? And here's my mixtape. And that all came from the database. That should be seeded for you automatically via our seed script, which sticks the post data in the database when we set up the project in the first place. So anyway, that's how that bit works. Any questions about params or how the database stuff works, we can briefly talk about. Okay, cool. So params, hooray. Every app needs them. Pretty straightforward. So we can kinda skip over that. The next exercise is nested routing. I think, what I wanna make sure we cover is nested routing and mutations. If we get to progressive enhancement, that's nice, but these two are the most important. So we're absolutely gonna cover these two, at least. So for, yeah, number four. Make sure that you're in the right directory. So you're in for nested routing. Check out the README there for the start and stop, and run node dev exercise 04. And so you should be on port 4004. And that is where we're gonna be working, okay? Any questions before I let you off on this one? Okay, cool. I'll answer, Katerina just asked this question about automating the params check in loaders. Yeah, we don't have any plans to automate that check for you. Yeah, the check isn't really, in my experience in building Remix apps, it hasn't been a problem for me. It's literally just this, and I think it's worthwhile. Automating that check, it would be difficult because it would involve I guess, the problem is we'd have to teach TypeScript our convention, and that's not gonna happen. Like, so you could automate the check for the runtime, but you would never be able to automate it for TypeScript, and TypeScript is worth a line in your loader. So yeah, we probably won't ever do it. Wouldn't it be an idea if, yeah, you know the slug has to be the dollar, whatever you put behind it. If that's not in the params, then it's kinda invalid, no? Yeah, yeah, totally. So we could do a runtime thing. But again, even if we did that, you're gonna get a type error right here because TypeScript doesn't know that you did that. Fair enough. Yeah, so, so we'll probably, there'll probably never be something like that. Several people finished, so let's do this. We are adding an admin section of our app, and so we're gonna have a link to that. We will, I think, I can't remember if this is the section where we actually lock it down to logged in users. I think that's in the homework. So you can do that later.

Creating Admin Section with Nested Routing

Short description:

We create an admin section with nested routing in Remix. The admin route handles all admin-related routes and subroutes. We render the outlet for the child routes. When there are no child segments, the index route is shown. Clicking on 'create a new post' displays a 404 error because the new.tsx file is missing. We create the new.tsx file and render the 'new post' content. Multiple nested routes can be used without blocking each other.

But yeah, we just need to get the admin section created. So this first bit is a bit of a routine for us. We've done this already. So if we go to the posts index, we'll just put this right underneath posts. And go localhost 4004. There's our admin posts. And this is say, posts not found, because that is not a post. And so we're gonna make a admin.tsx. And so now we've got a route to handle that. And so instead of falling down to the slug, it will actually just go to the admin page. So let's go over here, and grab all the JSX, and other stuff that we already know how to do over there. And if we come over here, and refresh, then boom, we've got a list of our posts. And then we've got a dot, dot, dot over here on the right side. So this is gonna be the parent route for all of the admin routes and subroutes beyond that. So we've got the post listed here, and you'll notice that the link is pointing to the post.slug. So there's no route combination here. We don't have to do slash whatever and combine this. We just want to have this link go from slash post slash admin to slash, post, slash admin slash slug. And so this is where relative routes come in. And that's a React writer Rev6 feature. Of course it's in Remix as well, and so that's awesome. But to be able to handle that, this is going to be a sub route to the admin route itself, and so this is where we want the child routes to go. So we render that outlet there. Okay, so we bring that in from Remix run React. And so when I click on these different routes, I'm going to get a 404 not found because I don't have a route to handle those. Whoa, I just lost that. Sometimes when I swipe left and right like this, my screen gets locked and I don't understand why. Okay, anyway, so that gets us to there. We're going to make our index route for when we are actually not at a child route. So if we're just at admin, what do we want to have show up here? And for us, we're just going to say, create a new post. So I'm going to make a directory called admin and then a file there called index.tsx. So now if I come over here, we have this showing up in there. So this is like nested routing, right? We've got our admin route. That's our layout for everything. And then our nested route inside of that, that is the index of what shows up if we don't have any children, then this is what's going to show up. This create a new post. If I click on that, now I'm going to get a 404 because I don't have a post or a file at new.tsx. So let's create that one, new.tsx. And then coming over here. Oh, right. I already did the outlet thing. So now we were saying this is where my children go. Now for our new post, this is all that we care to put in there. So new post. Hooray. There it is. Sweet. So if I go to admin, I'm going to see this as my index route. So I have no, there are no child segments. So show this. And then when there is a child segment, it is going to add slash new, then the new sub route is what's going to be shown. Okay. Cool. I think that's as far as we went. Is that as far as we went? Yeah, I don't think we went into that. Or did we go into the actions? I can't remember. Where were we? We said stop at actions. Yeah, okay, cool. Yeah. So what questions do you have about nested routing? Can I have an example of that? I might have one. If you have multiple nested routes, they're not blocking or are they? So yeah, and actually we do, we do have multiple nested routes. So we've got our root route that actually... We didn't look into that, but if you look at the root TSX, that's responsible for rendering everything from the open HTML to the closing HTML. So like the entire page. This is actually really cool, because in my site I control a class name based on state. which is kind of fun. Normally you have to use a use effect for controlling anything in the HTML, because we like hydrate just the body or whatever. But yeah, so you're responsible for all of the things and this renders an outlet. And then when we're on the post page, we have what we don't have it here because we're just leveraging the default, but that just is another outlet. And then on the admin, we've got our third outlet.

Handling Post Requests and Creating Posts

Short description:

On exercise 5, we continue from the previous part and discuss handling post requests and creating posts in the database. We use the form data API to access the form data for the request and create a new post with the provided title and Markdown. We then redirect to the admin page. Remix allows us to easily handle post requests and perform database operations.

So we've got four routes that are being rendered on the page. One for the root, one for the posts, one for the admin and then one for the new. And so yeah, on the server render, every one of their loaders are going to block the initial page load. And so most of the time that's what you want because you want to just load up the whole page without any loading spinners in place. But sometimes you've got some data that's really slow or not important or whatever. And so that's what the deferred API is going to give us in the next couple weeks or you can just use the current feature of UseFetcher and have that just load in the client.

I think I may have missed your question though. Does that answer the question you had? Yeah, it mostly is. So if it's blocking or with the deferred, if you use Max's data to fetch your parents data in the UI, you would have to consider if it's fetched or not or am I mistaken? No, so by the time your component is rendered, all of the data is done. Okay. The exception to that is the deferred API. So if you are using the deferred API, there's a special component that you use for what should be rendered while we're waiting for that data to become available. But yeah, yeah, I guess let's move on to the next exercise. So we're on 05 mutations and we're gonna start from actions and go down to progressive enhancement in our developer blog so we'll just continue from where we're at. I give you the whole form route like again, we're not here to learn React and JSX, we're here to learn Remix. And so yeah, the only Remix bit of this is the form component and we'll talk about that when we get back together. And then we've got some code for you for the Prisma stuff. And yeah, then we do some fancy things with type scripts and error handling and stuff like that too. So I think you'll enjoy this one and this really highlights a lot of the cool things about Remix as well. So yeah, just have a good time on that and I'll leave another message in the discord for you letting me know when you're finished with this. I wanna make sure I'm on the right spot. Are we continuing on this, or are we moving on to one of the other exercises?

Yeah, go to exercise five. The nice thing about the exercises is if you like get something wrong, but you don't know it, you just skip to the next one and it doesn't matter. So. Thank you. Yeah. Yeah, so it should be running exercise five. It should be on port 4005. So let's do this mutations stuff. First we grab this form stuff and what we're left with is the form right here. So this is neat because it's all just like nested routing is amazing. It really makes a big impact on how easy it is to build these UIs where you don't have like a layout component or anything like that. So this form just shows up in each of these. We have the titles like in Markdown. Let's just take a look at what happens when we submit this without doing anything else. If I say new title, this is great. Maybe we could generate that at some point and new stuff when I hit create post, we're gonna get a 405 method not allowed. And that's because what we did or what Remix does is it makes this post request and that post to the special URL at the current URL with this query parameter that defines what the route that submitted the request was. So this is how Remix can route that to the action that we defined in here but we didn't define an action. So there's nothing here to handle that request and that's why we got a 405 not allowed because we handle gets if we want to land on this page but we don't handle a post. So that's what we're gonna do is we're gonna handle this post. So we're going to, actually here, let's make sure I'm following along the right way. So yeah, first we wanna make a way to create a post in the database. So we're gonna go up to our post server in the models. Just add this create post, we can add typings later. And then we're going to add this action and let's talk about this here. So we grab create post from our post server and we have our action that accepts a request. So we're gonna get a red squiggly here until we add some typings. So I'm just gonna add those typings right now cause the red squiggly bothers me. This is action function and that comes from remix run node. And so now our request is just the request object. And as a reminder, this request is a web fetch request. So if we dive into the definition here and we go into requests, this is literally coming from the fetch API interface for requests. Like this is the global request object. And so that's probably filled for us on node unless you're using like the latest version of node. In fact in that, I think we don't even probably or I think we probably fill it there too maybe because for reasons. But in any case you can treat this as a regular web fetch thing. And so a lot of people who are using remix find themselves spending a lot more time here than they do here. More time on MDN than you do on the remix docs. This is actually really great because it means that as you're becoming or using remix you become a better web developer and not just a better remix developer. And this is what I liked about React. I felt like I was becoming a better React developer or better JavaScript developer not just a better React developer compared to what I was doing with AngularJS and stuff. So yeah, so this is a web fetch thing and so we're going to use the form data API. So again, if you go to and type and pass in anything then this will actually do a quick search and it will redirect you to the page on that matches that thing, the FACET. So I go to slash stuff all the time. That's how I do searches for the web stuff. But anyway, request.formdata is an API that gives you access to the form data for the request, which is perfect. So now we have the form data object. We can get the title, SLAG, and the Markdown and create the post with that. And then we are going to redirect to post slash admin because that was the thing that made the most sense.

Handling Post Creation and Data Updates

Short description:

We can redirect users to the finished page after creating a post. Remix handles the redirect by requesting data for the new page and then sending the user there. This eliminates the need for manual data updates and simplifies state management. Remix automatically revalidates data on mutations, ensuring the latest information is displayed.

We could also potentially redirect to post slash, you know, post, or yeah, SLAG. So take them to the actual finished page, like the post they just created. But maybe they want to write another post, I don't know. You can do whatever you want to there to redirect. But that's the basics of handling that. And so if we come over here and actually before I do this, let's take a look again at the index or sorry, the admin route, our parent route. This is rendering all of our posts. Now, I want you to pay close attention to what code is not here. There's no code in here for updating the post when we create a new post. There's nothing, okay. We're just like whatever post I have, render those. And then in here we have some code for creating a new post and then redirecting to post slash admin. So if I create a new post, we're gonna say, Sandrina is great, and then ah, Sandrina, ah, knows awesome stuff, ah, about accessibility. Okay, now we say create post. I want you to watch the list over here and see what the network tab is doing. Create post, boom, there it is. So we've redirected over to post admin. We did the post right here. Ah, and Sandrina is great, is on the left here now. So I have no code. Remember, no state management. You're not thinking about that. It all just updates automagically. It's amazing. So we create the, we make the post and then Remix says, oh, this is gonna redirect. So we look at the response. Well, yeah, this is an XRemix redirect. So because Remix is just emulating the browser, you're not actually gonna get a redirect right here because you don't want the browser to go like, re-fetch this page. And so Remix is like, oh, you want them to go to this post admin page? Well, I happen to know that the admin page has a couple of routes that have loaders on it. So let me go and request that data. And if you look at this, that's happening all at the same time. So I'm gonna go request that data. So then I can, at that point, send the user over to that page. And so this is really awesome because it means that we don't ever have to keep our data up to date. And that's why you don't need to worry about state management. So when a mutation is made, Remix is like, okay, all bets are off on the data that we have here. Let's go and refresh all of the data on the page. Now, I know some of you are thinking, oh no, I can't refresh all of the data on the page every time the user likes a tweet or something. There's absolutely APIs for you to be able to say, hey, opt out of this automatic data update. But keep in mind that the way that the browser has always behaved is when you make a mutation, you're doing a full page refresh. You make the mutation, and then you create brand new HTML based on whatever's in the database currently. So you're always getting the latest up-to-date information. So that seems like a pretty solid default. And this is what allows us to keep the mental model of the late 90s, early 2000s of building web apps is because Remix automatically revalidates your data when a mutation is made. So that is mutations with Remix.

Complex Controls and State Management

Short description:

For more complex controls like a radio group that requires state for styling, useState can be used in Remix. However, Remix eliminates the challenges of managing application state in React apps, such as race conditions and complex state updates. In the future, with has in CSS, even more complex controls may not require state.

So what I was wondering when working with forums, how to do like more, and I'm clear on how to do a simple controls like a text input, a checkbox. But if you want something more fancy like a radio group, which not just simple radio buttons, but if you have something where you need state to do the styling. I'm thinking of something like gonna put a link into the chat, something like the headless UI radio group. How would you do that? Because as I see now in the forums, we always have these simple controls without any local state but requires state, because you need to style all the elements around it, right?

Yeah. So first I'll say that I think we'll be able to do this without state in the future because haz is coming to CSS. And yeah, that'll be sweet. But yeah, right now that's not possible. So yeah, there's nothing in here that I see not working nicely with remix. You can totally use useState and with remix, that works fine. So, yeah, I'm not saying that you will never use useState in remix but you don't need it for the app to work. What's hard about state in React apps is not this type of state. This type of state is easy. Anybody can do this. The hard part is application state where it's like, well, I just added this post and so now I gotta go update that list and all of the state that's managed there. So now we've gotta share some context thing. And now we've got reducers and dispatches and stuff. And oh, well, what if I update it before it actually is finished and then the request is canceled or the user... There's a race condition. Like there are so many things that make this really hard. Remix eliminates that hard stuff. Easy stuff like this, yeah. That you can still totally use that and it works just as well. But yeah, in the future, we'll have has in CSS and even something like this, you wouldn't need state for which would be sweet. So great question, Felix.

Handling Form Updates and Error Handling

Short description:

To update the value of a form, such as radio buttons or checkboxes, you can use the useState hook in Remix. By setting the input type to hidden and providing a name and value, you can serialize the option along with other form data. Routes and templates in Remix are similar, but Remix offers more flexibility as it allows you to use JavaScript functions as components. To load additional data based on a selection in a nested dropdown, you can use the useFetcher hook in Remix. This hook allows you to make a post request to a specific route, handle the request in an action, and update the options based on the response data. Error handling in Remix can be done by sending an error message from the server to the client and displaying it to the user.

That's a good idea. I'm just still not sure how would you update then the forms, the value? Because if you have a simple control like radio buttons or check boxes, then the browser keeps the state of whether it's checked or not. And if you control the state, then how do you pass it to the form, so to say? Yeah, great question. I would be very surprised if they don't have an actual radio button somewhere in here. Because if they don't, then it's very difficult to make this accessible. So there's probably a radio button being rendered somewhere. If there's not, oh yeah, they have a role radio. Yeah, that's unfortunate. I don't know why they don't render an actual radio button. But here's what you do. You'd say, here's my use state, or we'll say option. Oh, great, I messed it up. Okay, let's try that again, use state, option. So like, here's my sandwich or my sandwich, sure. And then inside of here, you'll just say input, type, hidden, name, option, and then value as option. And so now it will be serialized along with everything else. Okay, that's a great idea, cool. Yeah, some of the folks who've been around for a couple of years are like, oh yeah, I remember when we used to do that. That's true. Yeah, bringing it back, cool. Great question.

Other questions, I see the MightyHake, is that how you say your name? Yeah, very good. Yeah, I'm not sure if it was answered before but sort of like routes and templates are sort of the same thing here, no? And what if you wanna use a template under a different route? Yeah, so what's cool about this is that it's more powerful than templates because it's just JavaScript, it's like a full language, and so if you're like, you know what? I sure like this label thing, then you just turn that into a function component. And so you're like, oh, I wanna make this a function called, yeah, my label component and now you can use it anywhere. All right, great. Magic React is pretty, pretty sweet. Cool, Chris?

Hey, quick question about how you would go about a nested dropdown. So let's imagine an example. You have a list of countries and then once you select a country, you would like to have another dropdown, load data for all the cities. I mean, it's pointless example, but how would you go about loading additional data to the form based on this selection? Yeah, that's a super question. You're getting into more advanced stuff. But I'll show you what we do. This is the material for the advanced workshop, which I am giving tomorrow and the next day. So, and they're probably still tickets maybe. So if you want to sign up for that, it could be fun. But yeah, so if we go to... Oh, where was it? Yeah, this is the route. So this, I'm using... I've got actually here, I'll show you what it looks like. Fake books. We go to sales. And if I want to create a new invoice, now I've got this combo box. And it's data driven based on the customers that I have. And so you can do the same thing, like multiple of those are selects based on what's the previous option. But all of that, that's going to take a UI state that you're managing, right? So when they select this, now let's go update these. But the way that this works is we've got this combo box component that's using this useFetcher hook. This is from Remix. And useFetcher will be submitted. So it will do a post to this Action, which actually is where this file is. So where this component is. So it will just post to this route. And it will post the query. And so then the Action that's in here will get called and we can talk to the database, do whatever we want, send back the response. And then when that Fetcher has data, that's what's going to be used to power what options are available. So you just do that exact same thing. This is actually not a very normal convention or it's not a documented convention because when I was preparing the workshop material, I just had this idea that like, what if we took the resource route and put the component that uses it in the same file. And so, yeah, the fact that we're exporting this, that's not a convention. Like you can export whatever you want from a module. And so we just import this into, where is it? I think it's, yeah, customers, or no, it's invoices new. Yeah, if we go to the customer combo box and then we just render this and then we can send over an error for like, so when this is submitted, if that error, or if there's an error submitting that we send the error to the customer combo box so it can display it nicely or whatever, but yeah, it's totally isolated from the rest of that page so it's all like, here's everything for this component, including the back-end code for this component, all in one place, it's really nice. So yeah, the short answer is use Fetcher. Okay, cool, thanks. Cool, any other questions? Yeah, we got an elaboration on the question from earlier. So I'll get to that one first. I was wondering if you could do an error handling with createPost function. I'm still confused, so are you saying if there was an error creating the post? Yes, exactly. I'm sorry I'm not articulating the question right. Yeah, that's okay. I was wondering if you have to handle, if, for example, if Prisma has an error, where do you handle that, and how do you transfer that back to client to be able to handle it, show it to the user? Yeah, totally.

Handling Errors and Redirects

Short description:

When handling errors in Remix, you have the option to use an error boundary component to catch and handle unexpected errors. The error boundary can be nested within the component or route where the error might occur. By adding the error boundary, you can prevent the error from affecting the entire application. Redirecting after a mutation is not necessary for the UI to be updated. When a successful mutation occurs, Remix automatically refetches all the data on the page. This ensures that the UI remains up to date without the need for manual updates. If a redirect is not performed after a mutation, the browser may prompt the user to resubmit the form if they navigate back to the page. This behavior can be avoided by using a redirect after a successful mutation.

I was wondering if you have to handle, if, for example, if Prisma has an error, where do you handle that, and how do you transfer that back to client to be able to handle it, show it to the user? Yeah, totally. So you've got a couple of options, and in this exercise, we go into the action data response here for the errors for what the user entered. But you're talking about, like, what if I'm the one at fault? Like, I made a mistake. So we don't talk about error handling in this guide, but the JokesApp tutorial goes into both expected errors, like 401s, 404s, and things, and the unexpected errors. So, like, something blew up, I messed up. And the solution is, let's see, is to use this thing called an error boundary. So you export this function component that's an error boundary. It says, I messed up, sorry, whatever. And this is also gonna be nested in place of whatever thing it's wrapping. So, actually, on the Remix website at the very bottom is where we talk about errors. So you add the error boundary and in place of the component or that route, it's gonna render your error boundary. So that's how that works. It also bubbles up to the lowest common error boundary. So that's how you'd handle that. If you wanted to try and catch that error so that it doesn't blow up the whole thing and remove the form and stuff, because that is pretty reasonable, then you would just follow the same pattern here. You'd maybe do a try catch around that and then send back an error message that says, hey, sorry, I screwed up something, whatever. So, yeah, if you were like, yeah, my database is pretty unreliable or something like that, or in this case, we actually don't handle the case where the slug is unique. So if I tried right now to create a new post that has the same slug as another one, then that's gonna blow up something as well, and this is what I get without having an error boundary in place. Yeah, that would be a thing to probably check for. Perfect, yeah, that just drew a perfect line to your Epic React course that makes sense now, with the error boundary, yeah. Cool, thank you.

But I was wondering if you wanted to do something without a form, but you have a button that's an action, can you do it without a form? Because I guess Remix is following this old-school web model, like you say, where the state is managed through the form on the server and whatnot. I was just, yeah, wondering. If, for things that don't need a form, but you still want that same kind of behavior. Yeah, that's a great question. I was gonna show you in this, but I think I shipped it back to that. So let's run it locally so I can show you what I mean, or show you an example of this. So I'm gonna start the dev server, and it starts so fast. Have any of you noticed how fast the dev server starts? Isn't it great to have a dev server that starts really fast? I think it's great. Okay, so if we go to deposits, we're gonna see a little trash can icon right there. So it's like the button. And so I'm gonna click on that and it deletes it. That's a mutation, right? So how does that work? Well, as it so happens, this is just a form. So we still have a form, and we're just wrapping the button, or the trash can icon in a button, and that is the submit button. So you'd be surprised how many things can just be a form. And that's the way that we used to do it back in the early 2000s. So keep in mind, actually, that the method is still POST, even though we're actually deleting, because the browser only supports GET and POST. You can totally do DELETE here, it is supported, but that runs contrary to progressive enhancement because the browser doesn't support that. So we just do POST and then we handle that ourselves. But in any case, so that's how you do that if you have some visual thing. But your question leads me to another common question, which is what if I want to do a mutation that doesn't involve user interaction at all? So on my personal website, I've got on my blog, when you oh, looks like Red is in the lead. Huh, that's new, congratulations Red. When you go to one of my blog posts, if you read the whole thing, you scroll through the whole thing, then I count that and you're on the page for long enough, then I count that as read and that's what goes into the rankings for the different teams based on what team you're on. And also I keep track of how many reads there are total of weather or not you're logged in. And I don't want you to have to like click a little checkbox to say that you read it. So we need to do a mutation that happens without you actually doing anything. And so the way that I do that is with useFetcher. And so useFetcher has this, the ability to create forms for that particular fetcher, which we're doing for this delete button, but also the ability to say.submit. And that's what we saw earlier with the customer thing. Where's the customer and the resources here. So in here we say, hey, fetcher, I want you to submit so that you can actually do that sort of submission without any user interaction. So yes, your use case is handled.

Quick question, so in our example, we do a redirect after we finished creating the new post. Is that the reason why the list of posts is getting updated? If not, can you maybe quickly explain what is going on under the hood? Yeah, the reason we redirect is just because when a user is finished with their post, we just figure they don't want to make a new one, but maybe they do. So, you do not have to do a redirect for the UI to get updated. The reason the UI gets updated is when there's a successful mutation, Remix will automatically refetch all the data that's on the page, that's it. And so that's why you never have to think about keeping your data up to date. What would you return in place of that?

All right, so this is actually a great question because if you don't do a redirect, let me move this out of the way. If you don't do a redirect, when there's a successful mutation, then you wind up in that funny place where the browser, like the user's hitting the back button and the browser pops up this thing that says, do you wanna resubmit this post submission? And that's super, super annoying that it does that. This is only if JavaScript is disabled. If you did a fetch request, that's not gonna happen. But with, so taking a step a couple of years back, that's why we have that super annoying thing and that the reason for that is because they didn't do a redirect. And so that means that the post request is part of the history stack. So you have get, get, get, post, get, get, user hits a back button. They do a get. User hits a back button again. They do a post. And so the browser, like for a long time, that's just what they did. And so you'd book two flights or something. And so then the browser vendors were like, oh, that's probably not good.

Post Requests, Error Handling, and JSON Data

Short description:

When doing a post request, it is recommended to redirect to a get request to avoid multiple get requests for each mutation. Usefetcher can be used for this purpose, as it does not perform a transition. Error handling can be achieved using catch boundaries, which render fallback components when data loading fails. When nested UI is used, only one fallback will be rendered on the page. It is possible to render a fallback with a higher scope by designing the catch boundary to render the entire section of the UI. The progressive enhancement feature in Remix can be explored independently. JSON data can be posted to an API endpoint instead of URL encoded data by using a resource route and specifying request.json instead of request.formdata.

Let's like warn them. Do you wanna do this post again? And then now people just say confirm because they just want the thing to go away. And so they do book two flights anyway. So the solution to this is do a get, get. And then when they do the post, you actually redirect. So the post doesn't end up in the history and it's just a get instead. And so when they go back, then they get the post. Or then they get the get instead of the post. Anyway, we'll- So you redirect to the same page. Yes, exactly. But the problem with that is now you have, if you're doing a bunch of requests, a bunch of mutations, you're gonna have a get for every single one of those mutations. So you have a big long list of history stack that's to the exact same page over and over and over again. So that's not great. If that's the type of thing that you're doing, if you're like, I don't want to go anywhere. I just want to stay here, then that's where you use usefetcher because usefetcher does not do a transition. And again, that's going deeper than where we're going with this workshop. But the point is that, yes, we have handled this use case and it's a usefetcher thing. That's basically the advanced workshop is let's show you how to use usefetcher, that's basically all that it is. So that's in place of action? No, you'd still use an action. That's the cool thing about usefetcher is everything else is the same. So if we wanted to change this, we'd have my fetcher usefetcher and then we'd say fetcher.form and everything else works. And here, you can do a redirect if you want to. But normally if you're doing a fetcher, then you're gonna say Json so that the will be whatever you pass back in the Json thing. So yeah, the mental model stays the same regardless of whether you're doing a transition with a regular form or whether you're not doing a transition with a fetcher.

Okay, Marvin? Hi, I also have a question regarding the error handling, if this is okay. Yeah, so I never used Remix before, but I read in the break about the catch boundary which seems to be as the error boundary but for bad responses. And I'm wondering about our web app where the navigation and an outlet and an outlet we render many children and in each of a child we show data which has to be loaded but there could be something wrong and we have to show a fallback. So we could wrap all of the children and a catch boundary and show them a fallback if one of them fails to load data. So in case this is just the case once we show them one fallback and all the other children are still showing data we can use the navigation and so on, but what if all of them, all of the data loading fails then I would prefer to show just one fallback for entire outlet and not many of them. Is there a way to opt out of taking the closest fallback and then showing just one outlet so that we can still use the navigation and so on? Well, so keep in mind that when you've got nested UI you never have sibling outlets. That is not a thing in nested UI cause you don't have sibling URL segments either. Like you can't be in two places at once. And so you will only ever render one fallback on the page. And so if this part of the page is the one that had a catch boundary, like the user doesn't have access to this invoice or something. And you've got a catch boundary right there. It's gonna render right there. If you don't have a catch boundary right there then it's just gonna go up the UI, up the routes until it finds one of those routes that has a fallback or a catch boundary and it will render that one. Does that answer your question or am I missing something? Yeah, but I'm thinking about if you could go back to the previous slide, you have the catch boundary as it is shown there but also one about the left part of the invoice list and then maybe also about this money spend above right? So then most of the page would then show a fallback. And in my example I then just want to show one fallback below the sales heading. But if there's just one data loading fails then I still want to show the close one. But just in case the data loading for every request made on this page fails then I want to show the fallback with a higher scope which is high in the component tree. Gotcha. So the thing is that with at least the way that this UI is designed or the way that it works is that this section right here or this part of the UI is controlled by the invoices route. And then this part of the UI is the invoice ID route. And then this part of the UI is the sales route. So if any part of the data loading for either one of these components fails then you're going to render this whole thing as a catch boundary. So that there is no like rendering part of it in the catch boundary and some other part somewhere else. You could do that, you could make that work where your catch boundary renders basically the same thing and just renders as much as it possibly could. And so that is totally possible that you could do that. And it could even render the outlet so that the child, if it happened to work, that would work. I've just never thought about doing it that way. But yeah, so what you're describing, I can see that working. I've just never tried that.

Let me just double check that I covered everything I wanted. I know there are still some more questions, but let me just double check. Yeah, we covered mutations. The progressive enhancement stuff is cool. You can take a look at that yourself though. It's pretty simple. It's like what happens if the JavaScript takes too long or the request takes too long, and then it shows some pending UI and stuff. It's pretty straightforward. I don't think that's important for us to cover. So happy to just stick around and answer your questions, but yeah, you can put away the workshop because we're not doing any more exercises. So I will, and if you need to like take off and stuff, that's totally fine, just before you do, if you could look at the read me on the, like the root of the repo, there's a link to a workshop feedback form, and I'd love it if you could provide some feedback. Can you just post arbitrary JSON data at API endpoint to send the form data or does it need to be URL encoded? Yeah, you can totally post JSON data rather than URL encoded data, if you wanted to do like your own fetch. So you can make what we call a resource route, which is a route that only has an action or a loader, does not have default export. And in that, if you're posting JSON data, then instead of request.formdata, you'd say request.json, and there you go, now you got the JSON. So yes, that's totally possible. And you could do that in this as well, it's just that Remix by default will encode the form as the form is encoded, just the way that the browser works. And also we do streams and all that stuff too for like file uploads and stuff.

Handling Multiple Forms and Migration from Next.js

Short description:

Two different forms on the same route can be disambiguated based on a field of the form. Migration from Next.js to Remix can be done gradually using Express or Nginx to route to different apps. UseFetcher in Remix caches data using cache headers instead of in-memory caching. Remix may be compatible with React Native, allowing for the use of the Loader from React Native. The demo of the to-do app working with disabled JS and a 3G connection is impressive and should be featured on the landing page.

But that again, is right back to beyond what we're looking for, or what we're doing in a sub-workshop. And two different forms on the same route, that's a great question. I'll show you an example of that, let's see, form. Just looking for an example in here. Yeah, okay, so on, no, no, let's do the invoice ID. So on the invoice ID page of this app, if we go to this invoice, we've got, oh no, we only have one form on here. Oh, there's gotta be a page where we have multiple forms. Yeah, arbitrarily, so demonstrates pending UI. Shoot, okay, well, the solution is, I'm sure I've got an example, but I just can't find it, but the solution is basically to disambiguate based on a field of the form. And so here, if we look at intent right here, submit buttons can actually have a name and a value. And so if you had multiple submit buttons in an even in a single form, you can say intent. And this is delete deposit. So whichever one the user clicks on, that's what the value of the intent will be. And so then you can disambiguate which button they clicked on. But these can be totally separate forms too, and you just have to submit button be what the intent value is. And then your action can say what was the intent? Okay, I'm going to do this. Oh, they clicked on that form. I'm going to do this other one. So yeah, all just web stuff that has nothing to do with Remix. That's just how the web works.

Sandrina. Yeah, so my question was around the migration. So let's imagine we have a codeways in Next, GS quite large codeways, how would we migrate it to Remix. Yeah, that's a great question. So there are a number of people in the Remix Discord who are doing this right now. And so they can probably give you more insightful answers like actual experience based answers. One of them I know is using Express and having Remix or having Next be served by certain routes via Express will route to the Next app. And then other routes will be handled by Remix because Remix like plugs right into Express. You use the Express adapter. And so then as the user goes from the Next app to the Remix app or vice versa, they do a full page refresh to get between those two. You could do the same thing with Nginx or something. Just route to the different apps. And then over time you just migrate. That's probably the best one because the challenge is that Next uses a completely different router. Like everything else is like very different. And so there's not really a very straightforward migration there. I don't know if it's possible to use React Router with Next.js, but probably not. If it were, then you'd migrate to React Router first. And that would make things a little easier. But I think that's probably the best approach there is just either Nginx server that serves to the different apps or just use Express to serve to the different apps. And then migrate over time like that. The nice thing is that it's both React and so you don't have to rewrite all of your components. But yeah, like your data loading and mutations, you'll just find yourself deleting a ton of code. And I've done this before actually. I was one of the ones who worked on the Remix vs Next where we took their app for their E-Commerce demo and we rewrote it to use Remix. So I have done a Next to Remix migration and it's not super fun but it is super possible.

Does the UseFetcher cache the data? Would there still be a use case for something like React Query for example populating options like from APIs? So it does cache the data. So here's the thing. Stuff like React Query caches the data but it caches it in memory and so you can navigate, like go over here you fill this in and then you navigate to another page and you come back and it's already cached. That's why that's cool, right? What if you close the browser tab? Well now it's gone. It's gone forever because it was in memory. And so what Remix encourages you to do instead is to use cache headers to have the browser hang on to it in its own cache for as long as you want it to be cached. And so then when the user types in their query it will be cached over based on the URL for the query. And so yeah, that is the preferred mechanism for caching is just to use the platform rather than implement your own thing. So the use case that you're talking about is well covered. It's just covered with a different approach.

Is there any official word about making Remix compatible with React Native, not just sharing components but using Loader from React Native? So I mentioned that earlier. I think that it might be possible to have your View or your Loaders and Actions in the same file as your Views for React Native. This is not a priority for us but it is something that I think would be really cool and totally possible. So if anybody wants to experiment with that, be my guest because basically you could just have a special build like maybe a convention for instead of an app directory, you'd have a native directory or something like that. And then everything else would be the same except every route would be a resource route and then the form would just submit to the URL, like the full URL for that route or something. I think it's totally possible. I'd love to see somebody experiment with that.

The demo you showed earlier when you disabled JS and the to-do app still works fine with the 3G connection, pretty amazing. I think you should put it on the landing page. I agree. I actually think that that demo is awesome. And you want to see, check this out, the code for this, for that whole thing is all in one file and it is, actually, no, that's the playground.

Demo, Stacks, Caching, and Future Plans

Short description:

The demo is awesome. The code is all in one file, around 600 lines, including frontend and backend code. Remix simplifies development, reduces lines of code, and improves performance. Remix Stacks offers pre-configured scaffolds with tooling and user authentication. The community has created numerous stacks. Caching in Remix is use case dependent, and there is no API to clear browser cache. Remix focuses on the center of the stack, bridging the gap between client and server. It may expand to include more built-in support for database models. Prisma is already a great tool for database access. Reach UI is part of Remix and may be further developed. The future may include additional features like a mailer and queuing system.

I actually think that that demo is awesome. And you want to see, check this out, the code for this, for that whole thing is all in one file and it is, actually, no, that's the playground. Let's do the finished thing. This includes all of the optimistic UI, making it feel like it's crazy fast even though you're on a 3G connection. It's 600 lines of code. That includes frontend and backend code. That does not include the session management for user authentication. That's 90 lines of code, and the model, actually, no, I didn't have a model. I talked directly to the database in this file. So yeah, it's pretty much like you're gonna be, if you added all of the lines of code for everything all together, you'd be less than 1,000 lines, which is hilarious to me because that's way less than a lot of the actual to do MVCs where it was all just UI, frontend, local storage, no user auth, no race condition handling, no actual persistence, any of that. So we can do more with a lot less, and on top of that, because a lot of this is happening on the backend, we don't have to send as much JavaScript to the frontend, either. And so it also loads way faster, which is awesome. So amazing stuff you can do when you have a server.

If we go to Remix Runs stacks, this is what you're looking at for. So Stacks is a really cool feature where you can make your own custom scaffolds, but we have three official ones that basically come pre-configured with a bunch of tooling for ESLint, Prettier, BTest, TypeScript, Testing Library, MSW, Cypress, Tailwind, Prisma, like all of the things it has. User authentication built-in and instructions on deploying to fly. Everything's pre-configured. The Grunge stack will deploy to AWS Lambda. And yeah, it's really awesome. So that is what I would recommend is looking into Stacks. And what's really cool is that the community has shown up and if you look at the Remix Stack tag on GitHub, then you're gonna find a bunch of these. There are 54, I think, different stacks that people have thrown together. So it's bigger than just us. We expect that most people actually build one of these for their company. And then your company can just, like, any project that you need, you create a new project that has your component library already installed and configured and all that stuff too. So take a look at Stacks.

Oh, the TODO app. So let me show you where that TODO MVC app, it's Remix TODO MVC. Yeah, here's the code for that. Does Remix have an API to invalidate or clear the browser cache for an endpoint? So there is no API. The browser gives no API for clearing the browser cache for anything. But you can, if you need to, so first of all, I'll say that if that is a concern for you for some, that there could be a time or a given length of time where I need this cache to be gone, then you do not use cache headers for that for the most part. However, you can. You just add an additional query string parameter and now you get a new entry in the cache. So it is possible, but it's typically not the best approach to go about that. I found that caching, it depends on the use case. So we don't wanna talk about the approach as much because it's gonna be very use case dependent. So, yeah, there's no API for that. I haven't found a situation where I needed to have stuff cached on the client, but you can if you want to, and also you can totally use React query with remix if you want to. I just haven't found a situation where I felt like that was useful. And looks like Jonathan had a question earlier. Sorry, does the Remix team have some plans to expand more into full stack framework a la Rails? What, I mean, is more built-in support for database models and all that stuff? Yeah, so possibly. I think that Remix has nailed the hardest part of web development, and that's the center of the stack. The bridge between the client and the server. That's always been the hardest part. Rails is over here trying to shoot grappling hooks with their hotwire or their old TurboLinks, all the things that they've been trying to do. And if anybody has used for their email client, they know that that just does not work very well. And then Laravel has a lot of the same issues. I lose track, and Phoenix with their live view. And Laravel has their live something else. They're all of them, live wire I think is what it is. So they're all just shooting grappling hooks across from the server. And then next is over here on the other side and Gatsby and all these others shooting grappling hooks to serverless functions or whatever, API routes and stuff to the server. And so that's like this network chasm is the hardest part of the whole thing. And Remix has just solidified that, just nailed that. It's like a solid bridge across that chasm. It's actually, it doesn't even feel like a bridge. It's more like a teleportation device because you just are instantly there. And you don't have to think about it. And so that, that I think is the hardest part. And as far as like eating up the other sides of the stack, like going further than just the center. I think it's totally a possibility. The thing is in the JavaScript ecosystem, we've got some great tools for database access already. Like Prisma is amazing. So I'm not sure how valuable it would be for us to build our own ORM. I think we'd probably just wrap Prisma if anything. And the client side of things, like eating the other end of the stack, we do already have reach UI, that's part of what our part of Remix as well. And so we'll probably build more onto that in the future and make like a reach UI that is backend knowledgeable. So it has like some knowledge about the backend as well. And yeah, we could build like a mailer and a queuing system or like, I don't know.

Remix Funding and CSS Styling

Short description:

Remix has plans to make money through services around Remix or by building a product. They also have the option of getting acquired. Currently, they have sufficient funding for at least a year or two. Remix provides flexibility in loading CSS for specific routes and components, allowing for independent styling. It supports individual CSS files for each route, avoiding the issues of the Cascade. Tailwind is recommended for styling apps with Remix. CSS modules and Vanilla Extract are not currently supported, but there are plans to add support in the future. CSS-in-JS is discouraged in favor of Tailwind. The only deprecated TS type in Remix is the unused generic for useLoaderData.

So the stacks are what we put together because we don't have those things yet. And eventually the things that are built into these may get built into Remix in the future. But yeah, for right now stacks are good spot to start. Cool.

And there was another question I missed about Remix being open source. How does it make money? So we've got a lot of ideas on how to make money with Remix or with services around Remix, kind of like what Laravel does, or we could build a product, kind of like what Rails does with Basecamp, or we could get acquired. There's always that option. So if you work at a giant company like Microsoft or something, let me know. Cause that would be cool. Then we wouldn't have to worry about making money. Just somebody else paid the bills. But yeah, we've got at least like a year, maybe two, of runway, just from our seed round of funding. And so when we go to our Series A, we'll get even more. So I'm not super concerned about that. Our VCs right now are telling us to like, don't worry about making money right now. Just worry about getting users. So that's kind of where we're focused. But yeah, there's that.

Okay, what other options and strategies of loading CSS for specific page route and component? So for a route, we don't get into this for this workshop, but yeah, actually the TodoMVC app I do, I have this todos.css. So it's just a regular CSS file, but you'll notice if you go to the Remix TodoMVC, it actually takes you to the login page. This does not have any of this CSS here. Because here we have that HTML body of margin zero and all the buttons are supposed to look like this. I basically copy pasted this and then slightly modified it from the original TodoMVC. But I didn't want the CSS to impact the other pages. Like the login page. I wanted this to have tailwind on it because I didn't want to have to write any CSS. And so the way that we can manage that is that in the todo.tsx over here, we import that CSS file and we get a URL to the style sheet. And then we export the links. So these are the links that should be on the page when this route is active and it's gonna have that to-do style sheet. Well, when I'm not in this page, those links will not be there. And so that's why we don't have any of those styles on this page because I'm not on that route. So if we look at the HTML, we're not gonna see that margin or padding of zero, whatever. So this is because Remix gives us that API to determine what links should be on the page. So when I'm on the login page, I see this Tailwind.CSS, and that's because if I go to the login, I have this Tailwind CSS file here, but I don't want Tailwind on the page for the to-do MVC because that'll mess with the styles for to-do MVC. Do I have that up to do, see Remix to-do MVC. So when I'm on this page, Tailwind messes up those styles. And so you'll see that I have the link rel style sheet for the to-do CSS, but not for the Tailwind. So all of that to say that Remix makes the Cascade awesome. We brought back the Cascade and it has none of the enormous downfalls that we always had with the Cascade. The way that you would build CSS at scale at any big company I worked at was you'd have all of your CSS files and then a Sass would just munch them up together into one giant one that you put on all the pages. That was the easiest way to do it. Otherwise you had to add it to every page and that was like super annoying. But then you have like Cascade problems because you make one change over here and it changes all the things. So Remix makes it really nice because you can associate individual CSS files to individual routes and they are completely independent of one another. So, that's one way to style apps with Remix. The other is just to use Tailwind. So, that's what I do and it's amazing. I love Tailwind but I know not everybody does or maybe you have an app that you're migrating or something. So, we do not currently support CSS modules or Vanilla Extract, but we've got some really great plans to add the best kind of support for those things that you could imagine. So, that will happen eventually. But yeah, that does not happen yet. And then, yeah, as far as like CSS and JS, you should stop using that. It's bad for your users. It's slow. Yeah, I mean, it's not terrible, but because we now have these other options, these things are better. Just use Tailwind. That's what I say. So, eventually we'll have better solution or better support for other solutions that are not bad. Like, CSS modules is actually pretty good. I like CSS modules pretty well. And Vanilla Extract is also very interesting. And those things are not bad for your users. Those are fine.

Okay, what about the TS types is deprecated, just the unused generics? Yeah, that's the only one. It's that unused generic thing. Don't use the useLoaderData generic. The JSON generic is fine. It's just the useLoaderData one. We will always have something better to move to, and we will always give you time to move to it. So, don't worry about that.

Challenges and the Transition Manager

Short description:

The transition manager in Remix is a major challenge and a key component that handles cancellation requests, resubmissions, and revalidation logic. It is a complex and well-tested feature that allows for smooth transitions and efficient data retrieval. It works like magic, canceling unnecessary requests and providing the desired response. The transition manager is a crucial part of Remix's functionality.

Okay, what's the biggest challenge, hardest problem to solve when building Remix with the framework itself? I don't know. So, to be honest, I am in with like design sessions with Ryan and Michael and our other engineers when we're talking about new APIs and stuff like that. So, I'm involved in that phase. But as far as like the actual development, I have done very little in building Remix. So, I don't know if I'm the best person to answer that question.

Actually, you know what, probably the transition manager. Ryan and I spent a lot of time talking about that one. And the transition manager is the one that makes it so I can do this, slow 3G. And then I can come over here and go like, delete and boom, boom, boom, boom, boom, boom, boom. See all those cancellation requests and stuff. And if any of these have, if there's a resubmission or there's some sort of, I submit multiple while they're all going or something. All of the logic around which one of these requests to cancel, which one of them, when to revalidate, all of that stuff, the logic for that is pretty complicated. And it works really well. It's a very well-tested. I was involved in that. So there you go. It's all gone, like magic. Ah, it's so cool. Yeah, see, and we canceled all those because we don't need them. We got all the data we needed from this one. Ta-da! Here's the response. No, to-dos left. So why do we need those? We don't. So cool.

Watch more workshops on topic

React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Top Content
Featured WorkshopFree
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.

The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.

React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
React Advanced Conference 2021React Advanced Conference 2021
145 min
Web3 Workshop - Building Your First Dapp
Top Content
Featured WorkshopFree
In this workshop, you'll learn how to build your first full stack dapp on the Ethereum blockchain, reading and writing data to the network, and connecting a front end application to the contract you've deployed. By the end of the workshop, you'll understand how to set up a full stack development environment, run a local node, and interact with any smart contract using React, HardHat, and Ethers.js.
Vue.js London Live 2021Vue.js London Live 2021
169 min
Vue3: Modern Frontend App Development
Top Content
Featured WorkshopFree
The Vue3 has been released in mid-2020. Besides many improvements and optimizations, the main feature of Vue3 brings is the Composition API – a new way to write and reuse reactive code. Let's learn more about how to use Composition API efficiently.

Besides core Vue3 features we'll explain examples of how to use popular libraries with Vue3.

Table of contents:
- Introduction to Vue3
- Composition API
- Core libraries
- Vue3 ecosystem

IDE of choice (Inellij or VSC) installed
Nodejs + NPM
JSNation 2023JSNation 2023
174 min
Developing Dynamic Blogs with SvelteKit & Storyblok: A Hands-on Workshop
Featured WorkshopFree
This SvelteKit workshop explores the integration of 3rd party services, such as Storyblok, in a SvelteKit project. Participants will learn how to create a SvelteKit project, leverage Svelte components, and connect to external APIs. The workshop covers important concepts including SSR, CSR, static site generation, and deploying the application using adapters. By the end of the workshop, attendees will have a solid understanding of building SvelteKit applications with API integrations and be prepared for deployment.
React Summit 2023React Summit 2023
106 min
Back to the Roots With Remix
Featured Workshop
The modern web would be different without rich client-side applications supported by powerful frameworks: React, Angular, Vue, Lit, and many others. These frameworks rely on client-side JavaScript, which is their core. However, there are other approaches to rendering. One of them (quite old, by the way) is server-side rendering entirely without JavaScript. Let's find out if this is a good idea and how Remix can help us with it?
Prerequisites- Good understanding of JavaScript or TypeScript- It would help to have experience with React, Redux, Node.js and writing FrontEnd and BackEnd applications- Preinstall Node.js, npm- We prefer to use VSCode, but also cloud IDEs such as codesandbox (other IDEs are also ok)
Remix Conf Europe 2022Remix Conf Europe 2022
195 min
How to Solve Real-World Problems with Remix
Featured Workshop
- Errors? How to render and log your server and client errorsa - When to return errors vs throwb - Setup logging service like Sentry, LogRocket, and Bugsnag- Forms? How to validate and handle multi-page formsa - Use zod to validate form data in your actionb - Step through multi-page forms without losing data- Stuck? How to patch bugs or missing features in Remix so you can move ona - Use patch-package to quickly fix your Remix installb - Show tool for managing multiple patches and cherry-pick open PRs- Users? How to handle multi-tenant apps with Prismaa - Determine tenant by host or by userb - Multiple database or single database/multiple schemasc - Ensures tenant data always separate from others

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Summit Remote Edition 2021React Summit Remote Edition 2021
33 min
Building Better Websites with Remix
Top Content
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!
React Advanced Conference 2021React Advanced Conference 2021
39 min
Don't Solve Problems, Eliminate Them
Top Content
Humans are natural problem solvers and we're good enough at it that we've survived over the centuries and become the dominant species of the planet. Because we're so good at it, we sometimes become problem seekers too–looking for problems we can solve. Those who most successfully accomplish their goals are the problem eliminators. Let's talk about the distinction between solving and eliminating problems with examples from inside and outside the coding world.
Remix Conf Europe 2022Remix Conf Europe 2022
23 min
Scaling Up with Remix and Micro Frontends
Top Content
Do you have a large product built by many teams? Are you struggling to release often? Did your frontend turn into a massive unmaintainable monolith? If, like me, you’ve answered yes to any of those questions, this talk is for you! I’ll show you exactly how you can build a micro frontend architecture with Remix to solve those challenges.
Remix Conf Europe 2022Remix Conf Europe 2022
37 min
Full Stack Components
Top Content
Remix is a web framework that gives you the simple mental model of a Multi-Page App (MPA) but the power and capabilities of a Single-Page App (SPA). One of the big challenges of SPAs is network management resulting in a great deal of indirection and buggy code. This is especially noticeable in application state which Remix completely eliminates, but it's also an issue in individual components that communicate with a single-purpose backend endpoint (like a combobox search for example).
In this talk, Kent will demonstrate how Remix enables you to build complex UI components that are connected to a backend in the simplest and most powerful way you've ever seen. Leaving you time to chill with your family or whatever else you do for fun.
React Day Berlin 2022React Day Berlin 2022
22 min
Jotai Atoms Are Just Functions
Top Content
Jotai is a state management library. We have been developing it primarily for React, but it's conceptually not tied to React. It this talk, we will see how Jotai atoms work and learn about the mental model we should have. Atoms are framework-agnostic abstraction to represent states, and they are basically just functions. Understanding the atom abstraction will help designing and implementing states in your applications with Jotai
React Summit 2023React Summit 2023
24 min
Debugging JS
As developers, we spend much of our time debugging apps - often code we didn't even write. Sadly, few developers have ever been taught how to approach debugging - it's something most of us learn through painful experience.  The good news is you _can_ learn how to debug effectively, and there's several key techniques and tools you can use for debugging JS and React apps.