1. Introduction to Routing in React 18
Hey, everyone, I'm Delba from Vercell. Today, I want to talk about routing in React 18 and how it will change the way developers view applications. Routing has evolved from multi-page applications to single-page applications. Single-page applications bring a native app-like user experience. The client-side decides what content to fetch and render. Applications are transitioning between different environments, client or server.
♪♪ ♪♪ Hey, everyone, I'm Delba, and I work for Vercell. As some of you may know, we're the creators of Next.js, and I'm curious to know how many of you here use Next.js? Wow, so that's a lot of you. That's amazing. So, although we created Next.js, we also have a platform, Vercell. And funnily enough, Vercell supports over, I think, something like 35-plus front-end frameworks. So even if you don't use Next.js, you could still use Vercell.
And today, I want to talk to you about some exciting stuff we have been working on, which is routing. And routing in React 18. Now, as you may know, a few months ago, React 18 was released with new concurrent features. And on the React blog, the team mentioned that they expect concurrent features to have a big impact on the way that developers viewed applications. So today, I want to discuss with you what this impact could mean and how we will change how developers view applications, especially with React Server components, as well. And this may not also change things for Next.js but also for other frameworks and library maintainers. And I'm going to specifically look at routing, but I will also mention data fetching and rendering, or as I like to call them, the three pillars of the web, because those terms are very much interconnected.
Now, to give everyone here some context and also the people who might be watching online, I think it's important to just take a step back and look at how routing has evolved in frontend applications. Now, please note, I'm going to be condensing years of routing history in like five minutes, so there's a lot more nuance. And one way we can look at routing is through the type of applications we can build, multi-page applications, single-page applications and, more recently, hybrid apps. So in the very early web, routing was very straightforward because if you think about it, each URL mapped to a specific file on a server. And then later on, using dynamic server-side languages, we were able to generate a response from the server for a specific route. In a traditional multi-page application, routing is done on the server, and navigating between pages causes the full page reload that we're all very used to. Now, when native mobile apps came along, they brought smoother transitions and new UI patterns. And single-page applications, you could say, were the web's response to native apps. We wanted our websites to feel like native apps. That is to say, we wanted them to have the same user experience as native apps. In a traditional single-page application, routing is done on the client-side. So on initial load, you may see a white blank screen while the client-side fetches and renders the content. Now, when you navigate on a single-page application, the client will dynamically rewrite new content. And for a given route, the client is deciding what content to fetch and render. And last year, Rich Harris, the creator of Svelte, gave an amazing talk on the whole MPA versus SPA debate, where he discussed some of the pros and cons of each. It's a really great talk, and I recommend watching it if you haven't had a chance to do it already. But one takeaway from that talk that I want you to remember is that he recognized that there's an emerging pattern in our industry, where applications are transitioning between different environments, client or server.
2. Overview of Routing in React
And he called those type of applications transitional apps. React Router's approach to routing is what I like to call component-based routing. Next.js took a different approach to routing using file system-based routing. The React team gave us an early Christmas present that will allow us to move towards more hybrid solutions.
And he called those type of applications transitional apps. And transitional apps, they try to combine the benefits of both the client and the server, because if you think about it, why not both? So that's kind of like a broad overview of routing on the web. Now let's zoom into React.
While React didn't invent single-page applications, you could argue that they contributed to their popularity. The fact that React was and still is mainly concerned with UI and rendering means that the community has come up with a few different solutions for routing. And one client-side solution that quickly rose to popularity was React Router. Now, how many of you have used React Router before? Yes.
So React Router's approach to routing is what I like to call component-based routing where you use code to map specific components in your application to your URL path. And if you combine it with a tool chain like create React app, then you can easily create single-page applications. And this leads to I think not just the adoption of React itself, but also applications that were fully client-side rendered.
Now, in 2016, a few years later, Vessel introduced Next.js. And Next.js was created as a framework to help developers build server-side rendered applications. And Next.js took a different approach to routing. It used what I like to call file system-based routing, where files in your application map to your URL. And although Next.js felt very similar to a multi-page application, it actually used prefetching and client-side navigation to give applications a Spar-like feel, if you could say.
Now, another incremental step towards hybrid apps was the Next.js data fetching methods, like getInitialProps. And what these data fetching methods did was move the fetching outside of your rendering code or outside of your component so that you could fetch data both from the client and the server. Now, I'm focusing on Next.js today. But it would be remiss of me not to acknowledge some projects that are also working on routing solutions for React, including Shopify's Hydrogen, Remix, and also Redwood. So fast-forward a few years in early 2020, and the members of the React team were publicly discussing moving more rendering work to the server.
The idea was that if we are doing data fetching on the server anyway, could we move some rendering work to the server and, therefore, reduce the amount of code that gets sent to the client? Now, you can probably imagine the hot takes that followed that tweet. I think it can be best summarized as, this looks a lot like server-sided routing. Are we going back to MPAs? But to quote a not so serious meme from one of my favourite people on Twitter, this is less of a pendulum swing and more a spiral of incremental improvement. So switchbacks. Not purely SPA, not purely MPA, but a kind of spike in convergence towards hybrid. That benefits from both the server and the client. And each time this conversation was brought up, the reacting was careful to emphasize that they were looking for a hybrid solution. And one important thing to note here is that this hybrid solution wouldn't be creating additional requests to the server. It would take advantage of a request that has to exist anyways. So, in December 2020, the React team gave us an early Christmas present that will allow us to move towards more hybrid solutions.
3. Routing with React Server Components
That was React server components. We now have the primitives to address disadvantages of multipage applications while maintaining the same user experience. We proposed a new router for Next.js that enables routing, rendering, and data fetching on the server. With React server components, we can interweave client and server components. Breaking up routes into independent fragments has three main benefits: creating layouts, reducing re-rendering, and having more granular control over data fetching. By building a new route with React server components, we reduce code sent to the client, server workload, and loading time. Combining with concurrent features simplifies loading states and improves navigation experience.
That was React server components. When you combine React server components with suspense and the streaming, we now have the primitives or the building blocks to address some disadvantages of multipage applications or server side rendered applications while maintaining the same user experience we love about single-page applications or client-side applications.
But there is one last piece to the puzzle. And in the last few months, the Next.js team has been considering this. If we need to go to the server to do a trip for data fetching and now we're going to the server to do rendering with React server components, could we possibly also do routing on the server? Could we enable developers to build applications where routing, rendering and data fetching happens where it makes more sense? And could we give them conventions that are easy to understand but also allows them to move parts of their application either to the client or to the server?
So a few weeks ago, we shared an RFC. And in this RFC, we proposed a new router for Next.js. And this new router builds on top of React components and React 18 features. There's a lot more detail in the RFC and I won't go too much into the implementation details. If you're curious to know more about it, I do recommend reading it. But for now, let me just share with you something that I'm most excited about and that we've been working on.
So, if you think about it, with React server components, we can interweave client components and server components in a tree. This means that in a page or a route, you could potentially have both client and server components. So, with this new model, it becomes increasingly important to break up our routes into independent fragments which we call route segments. These route segments, they map to an already existing term, URL segments. Although we are breaking up the routes, we want to maintain file system routing because generally it tends to be a more intuitive way for developers to define their routes. And breaking up the routes like this, or as we call it, doing nested routing, has three main benefits.
The first one is that we are able to create layouts, and layouts have been a very long, like, going-back community ask. And the way that we want to define layouts is that a layout is UI that is shared across routes, and these layouts, they shouldn't re-render or lose state on navigation. It also means that components that don't change within a route, so a layout, we also want to make sure that they are still interactive as the user navigates between routes. Secondly, if we combine it with server components, that means that on navigation, the server only has to fetch and render the segments that have changed, and we don't have to re-render the whole subtree for that route. And thirdly, we can have more granular control over data fetching. So in Next.js, as you may know, currently we fetch data on the page level, and with this new model we can fetch data in the segment level. And since we already moved data fetching outside of the rendering code or outside of the components, what we can do now is we can eagerly initiate those requests in parallel. And this reduces what some of you may be familiar with, which is waterfalls. And overall, the amount of time that it takes to load the content of a route is also reduced.
So by building a new route with React server components, we're able to achieve three things. Reduce the amount of code that we send to the client, reduce the amount of work the server has to do, and also reduce the amount of time that it takes to do that work. Now, if we were to combine it with concurrent features, such as transition, suspense, and the feature off screen component, we can simplify the creation of loading states and improve the navigation experience. For example, if you want to use client-side routing and you are fetching as you render, you may have too many staggered loading states or spinners. So, really, what you want to do as a developer is consolidate them into fewer more meaningful loading indicators.
4. Improving Navigation and Routing in React
When using server-side rendering, it's important to include loading UI to indicate background work. Pre-rendering a small part of the screen improves navigation by providing immediate feedback. Stashing routes allows for restoring state when navigating. React's server components enable more performant and smoother experiences. Creating shareable experiences without breaking URLs requires simple conventions for complex routing patterns. The React team provides the primitives for building hybrid applications.
On the other hand, if you are using server-side rendering, you have to fetch and render the content before navigation starts. So your application will appear unresponsive as the work is being done on the server. So, in that case, you do want to include loading UI to indicate that work is being done in the background.
In other case, we believe the framework should provide an easy convention that will allow developers to create loading states. Now, we can also improve the navigation experience further by pre-rendering a very small but minimal part of your screen. So, this means that when you navigate between screens, the navigation will be immediate, and the user might see something like a cover photo or title before the rest of the content loads.
And in a similar fashion, and this one is one of my favourites, is that we will be able to stash routes. And what that means is that we can stop the previous routes and then pre-render future routes so that when the user navigates between the routes, we restore the state. Now, I'm running a little bit out of time, so I'm just going to skip these two slides, but... If you do want to find out more, we do have more information in the IRC. What I wanted to highlight here today is that there are many ways you can think about routing in React, and we have been thinking a lot about how we do routing in XJS. It's not about routing itself, but it's what routing will enable us to do. So, we'll be able to create more performant experiences with React's server components and also smoother experiences by improving navigation. We also want to make sure that we create experiences that are shareable, that don't break the URL. And to do that, we need to create simple conventions that will allow developers to implement more complex routing patterns. So, last but not least, before my time is up, I want to take a moment to give a shout-out to the React team, because they're the ones giving us the primitives and the building blocks for us to be able to build the next generation of hybrid applications. Thank you so much. APPLAUSE Thank you. You want to go? Yeah. APPLAUSE All right. It was a blast. I don't know exactly what new React feature it was, but there was a certain point, there was a new feature that was released, and we updated our app, and it felt so fast that I didn't trust it. It feels like every app is going to be like that now, right? And every now and then we get a new feature in React that's just... Yeah. Yeah. It's too fast. You don't believe how fast it is. That becomes the new normal, and you're like, damn, this takes a long time. Yeah. So thanks for your talk.
5. Next.js and Remix: Converging Solutions
Next.js is not adapting the vision of Remix, but rather moving towards a converging solution. Remix combines component-based routing with file system routing to create nested routes. This approach is also seen outside the React ecosystem. The industry is moving towards combining component-based routing with file system routing, borrowing ideas from each other for incremental improvements.
Great information, by the way. We're going to jump into the audience question. First question is from Martin. Is Next.js adopting the version of Remix on component-based routing here? Is it the same? Yeah. That's an interesting question. So when I think about Remix and the work that they've done in React with routing, they've done an incredible work. There's no doubt about that. And if you think about it, most applications use React Router, which are from the creators of Remix, as their backbone. Now, when I think about Remix itself, I don't really see it as adapting the vision of Remix as much as moving towards or converging towards a better solution. So what Remix has done really well is they've combined the idea of component-based routing with file system routing and created nested routes. Which makes sense because file system routing is easier to implement, but then with component-based routing, you can map your components to your URL path. I think that is a great approach, but I also see it outside of the React ecosystem as well like for example with StealthKit. So I think overall as an industry, we're just moving towards that solution of combining component-based routing with file system routing. And it's borrowing ideas, right? Sorry? We're all borrowing ideas from each other and trying to improve the whole ecosystem. Incremental improvements.