This talk introduces the new Flat Routes convention that will most likely be the default in a future version of Remix. It simplifies the existing convention as well as gives you new capabilities.
Remix Flat Routes – An Evolution in Routing
Hi, I'm Michael Parter. You may know me online as Kiliman, which is short for Kilimanjaro. I've been a big fan of volcanoes for a long time. I'm excited to be speaking to you today at RemixConf Europe. My presentation will be on remix Flat Routes, Evolution, and Routing. First I'll start by giving a brief introduction on the current routing convention as we know it today. Here's a typical routing structure. It consists of a pathless layout for public pages like the About page, a user section with its own layout which has a list view, a user detail view, as well as an edit page. remix uses folders to determine the parent layout. It is expected that the parent layout will include an outlet to render its children. In this example, there is no user ID folder because we want the edit page to use the same user's layout as the other routes. Since the edit page is not nested, we use the dot or flat layout instead of a nested layout. When you create a nested layout, remix requires you to create a folder for its children plus a file for the layout itself. Unfortunately, one of the drawbacks of this convention is that the folder in the layout file ends up separated in your editor since folders are typically displayed first. This can be annoying, especially on large applications with many routes. As you know, next.js recently introduced their own take on nested layouts. Let's compare their convention with remix. Here is the same routing structure as the previous example. Next uses parentheses for pathless layouts similar to remix's double underscore prefix. The one thing you'll notice is that Next requires a folder for every segment of the route. The leaf route is a file named page. If a route segment adds a layout, you create a layout file. With Next, layouts and pages are separate things, whereas remix doesn't differentiate. Again, looking back at our user edit route, Next looks for the closest layout, which is user slash layout. Another major difference with remix is that remix uses named exports for things like meta, links, headers, error boundary, et cetera, and Next uses separate files for each. This can result in a large number of files depending on your app's needs. It led one person on Twitter to tweet this screenshot. Granted, this is an extreme example. One of the benefits of a folder for each route, though, is that Next does let you co-locate supporting files in the route folder, things like css, components, images, et cetera. This is one of the main things remix devs have been asking for. We'll discuss how remix FlatRoutes supports this shortly. Now that we have that out of the way, let's discuss the new remix FlatRoutes convention. It's currently a separate MPM package, but will be included in a future version of remix as core functionality. So what are the goals of the remix FlatRoutes? Make it easier to see the route your app is designed. Just pop open the routes folder and they're all right there. Since the file system typically sorts folders first, when you have dozens of routes, it's hard to see which folders have layouts and which don't today. Now all related routes are sorted together. Allow co-location of support files with routes. You'll see how you can keep your styles, components, and other supporting files with your routes. remix refactored and redesigned friction. While code editors are pretty good at fixing up imports when you move files around and remix does have the tilde import alias, it's just generally easier to refactor code base that doesn't have a bunch of nested folders. remix will no longer require this. Additionally, when redesigning the user interface, it's simpler to adjust the names of the files rather than creating and deleting folders and moving routes around to change the way they nest. And finally, helps apps migrate to remix. Existing apps typically don't have a nested routes folder structure like today's conventions. Moving to remix is arduous because you have to deal with all of the imports. Also as we saw with Next's new nested layouts, flat routes make it easier to migrate from other frameworks to remix. So remix flat routes was first introduced as a gist by Ryan Florence back on April 7th of this year. He posted a link on the remix disk board and I just happened to see it and I was intrigued because I too was looking to simplify my routing structure. Less than two weeks later, I made the first commit implementing the flat route spec. The trickiest part was trying to figure out how to determine the parent layout when there were no folders. Simply a dot separated file name. We'll discuss how that was implemented in a little while. An interesting point is that it wasn't until a month later that next.js posted the first layouts RFC describing their new nested layouts convention, which is now available as an experimental feature in next.js 13. As I mentioned before, remix flat routes is currently implemented as a separate package. To add it to your app, simply npm install dash D remix flat routes. Since routes are determined at build time, this is a dev dependency and it's not needed at runtime. One of the things about remix is that although it's opinionated, it does have some escape hatches that let you add your own customizations. In this case, it's the routes configuration where you can add your own routes in addition to the default convention. Since remix assumes the files in the routes folder use the default convention, you'll need to tell remix to ignore all files in the routes folder. Finally call the flat routes function to scan the folder using the new flat routes convention. In the future, you'll be able to specify which convention you want to use directly through the remix config file. remix flat routes changes some of the file naming conventions that we're used to. For example, for pathless layouts, use a single underscore instead of a double underscore prefix. The params prefix remains the same, the dollar sign, but I'll show you how we can change that later. Instead of using folders for routes, the file name includes the entire route. So use the dot instead of slash to separate URL segments. And finally, to handle scenarios like our user edit route, the underscore suffix specifies the segment is not a layout because it has no outlet. I'll demonstrate this later. remix flat route supports two configurations, flat files and flat folders. Flat files will typically be used for simpler apps as everything's in the file name and there are no folders. Here is the same example we used before, but using the flat files convention. Notice that there are no folders. The entire URL structure is visible at a glance without having to drill down into separate folders. We still have to identify the index route, in this case, user slash index. Notice how we named the file users dot index. Leading underscore here is simply to sort the index route before the other child routes. As I mentioned, we're using dots instead of folders to separate the paths. But without folders, how does remix know what the parent layout is? To determine the parent layout, remix flat routes finds the longest matching prefix and that determines the parent layout. Here we see that public prefix of public dot about matches the public TSX layout. Same with users. Remember in the previous example where we didn't want the edit route to nest under user ID? There we use dots in the file name to deal with it. But how do you deal with that when everything uses dots? remix flat route allows you to use a trailing underscore on a parent route to specify that this is not a layout. As you can see in the edit route, the user ID should not be treated as a parent layout, hence the underscore. This ensures that users dot user ID prefix is not matched with that route and remix will then look to the next segment for a matching layout, which is the users layout. Although flat file simplifies the routing convention considerably, it does so with some drawbacks. For example, there are no folders. We cannot co-locate supporting files with our routes. Even if we were to add rules to ignore these other files, it would require you to import them with the full path, not exactly simplifying things. Flat folders look just like flat files. The main difference is that instead of the routing being a simple file name, the folder itself is the route name. The route file, index TSX, is located inside the folder. This is similar to the next JS page file. remix doesn't differentiate between routes and layouts. Again, layouts are simply routes without left. So index TSX or layout TSX mean the same thing. They're simply aliases and is mainly used to be more descriptive when looking at your routes. remix supports multiple aliases for the route file. You can use either index, route, layout, or page, as well as add the underscore prefix. This helps sort route and layout files to the top of the file list when co-locating files. As we stated before, one of the main benefits of the flat folders convention is to support co-location. Here, we are co-locating support files like css, components, server files, images, etc. The route file will now let you import these assets as a relative import. I've been experimenting with splitting my route file into a route and a route server file. The main reason is I use Zod for validation and the package is kind of heavy. By only defining and using the Zod schema in the server file, I guarantee that Zod does not end up in the client bundle. So now that we've seen the benefits of flat routes, how do we migrate our existing app? The remix flat routes package includes a migration tool. Simply run the command mpx migrate-flat-routes and specify the source and target folders. The folders must be different to prevent the tool from overwriting your original routes. You can specify whether you want flat files or flat folders. Here's a screenshot of the routes from the sample fakebooks app before and after migration. You can see that it lets you quickly see how your routes are structured without having to expand the folders. One way to verify that the migration went smoothly is to run the mpx remix routes command on the original routes as well as the migrated routes. Since remix-flat-routes creates the same route configuration that was generated by the default convention, the routes should be identical both in hierarchy and paths. The only differences are the file names and maybe some sorting. Over the past six months, as people have tried out remix-flat-routes, they've offered suggestions for improvements and other enhancements beyond the original specification written by Ryan. I've already started the PR process for getting remix-flat-routes into the core framework. Whether these enhancements end up in the core is still to be determined. And at the very least, I will continue to support these as add-ons that you'll be able to include in your configuration. One of the pain points of the remix routing convention, both the current and flat routes, is that the use of the dollar sign can be problematic when working with files in the shell. Since most shells treat the dollar sign as a variable prefix, this forces you to escape the file name when using commands. Considering this is RemixConf Europe, I suggest using the euro. Or if you have a typical US keyboard, perhaps the caret. Some developers want to be able to mount the remix app on a path other than the route. Perhaps they are adding remix to an existing website. Here you can configure remix to use a different path, such as my-remix-app. This will update the route path and you won't have to add a separate folder in the routes folder. Jacob Ebi, a remix team member, recently created a sample app that had routes processed by different backend technologies, which is quite mind-blowing. He had routes from node, cloudflare workers, as well as service workers, plus a common set of layouts. He used RemixFlatRoutes, but in order to support his needs, he had to manually combine the routes into a single namespace to ensure the parent routes were correctly defined. In RemixConfig, you can now not only specify a single path to your routes folder, but pass it an array of paths. FlatRoutes will build up the route structure across multiple route folders and merge them into a single namespace automatically, so you don't need to do that manually. And as you can see from the MPX remix routes output, that the routes were correctly generated. So in conclusion, I've been a remix supporter since day one, when it was a paid product. It was well worth it and has definitely improved the way I build web apps today. I can't believe it was almost a year ago today that Michael Jackson first announced remix version 1 to the world, making it available as open source. In that short time, there have been two remix conferences, one back in May that I was fortunate enough to attend, where I was able to meet in person for the first time people like Michael, Ryan, and Ken, and many others that I've only known on Twitter or Discord. As well as this conference here today, where I'm grateful to be able to give a presentation and talk about something I've built and I'm very passionate about. And now with the recent news that shopify has acquired remix, we can all rest easy that this amazing framework will continue to prosper. I can't even begin to imagine the wild ride that Michael and Ryan have been on these past 12 months, and I'm excited to see what the next 12 months will bring. Thank you for taking the time to watch my presentation.