Interviews With Experts Bonus 0 exercises
    interview

    Matt Brophy on Remix, React Router, and Open-Source

    Matt Brophy, a core member of the Remix team, discusses the evolution of Remix and React Router, as well as his experience with open-source development.

    Matt joined Remix in 2022, with a background in web development and experience working on e-commerce sites such as Urban Outfitters. He was drawn to Remix's approach for data loading, sessions, and other abstractions compared to the custom SSR architecture he had been developing with another framework.

    Matt talks about the construction of the framework-agnostic Remix Router, which simplifies the internal architecture by focusing on routing and data loading rather than UI concerns. While other frameworks are unlikely to use Remix Router directly, its patterns and ideas have had an impact on the overall ecosystem.

    Matt is excited about the future of React and goes into new capabilities in React Router v7 and Remix, such as lazy route discovery ("Fog of War") for optimizing large route trees and single fetch for simplifying data loading and enabling full pre-rendering. He also discusses the possibility of merging React Router Fetchers with React 19's new form primitives and actions.

    Matt points out the Remix team's commitment to ensuring smooth migrations and providing developers with the tools they need to gradually adopt new features and patterns.

    Resources

    Loading interview

    Transcript

    00:00 Hello, everybody. My name is Kent C. Dodds, and this is Matt Brophy. How are you, Matt? I'm good. How about yourself? I'm stellar. Just super happy to have you. So, Matt and I go back to Remix. I was working

    00:15 at Remix. I'd just barely started, I think, when we started looking for some really experienced engineers to help us build Remix. And so, I interviewed Matt, and I went back to Ryan and Michael, and I was like, yep, we absolutely want this guy. Appreciate that.

    00:32 Yeah, absolutely. And you've been just like, you're very, very, if a member of a team can be core, like, you're as core as they go. Just a very influential and important part of the Remix team, particularly working on the router. Super, super happy with all the

    00:52 work that you've done there. So, yeah, that is my history with you, but you've got more history about you. So, can you give us a little intro to yourself? Yeah. I don't even know where to start. I'll start with the pre-Remix world, I guess. I

    01:08 did web development for a long time. I started in middle school, but I got into it professionally couple years after I graduated, got a job I hated, went back for a master's, worked in some non-hands-on coding jobs for a little bit. But about three or four years after I

    01:25 finished my graduate school, I dropped everything, found a dev job, took a huge pay cut, because I was like, I just want to be coding. That's all I want to do. So, I worked a couple agencies and things, ended up eventually at Urban Outfitters. I was there for seven years doing their e-commerce

    01:41 sites. We switched from an old Python server rendered app to an Angular checkout app, which I worked on. Then we went to an in-house SSR setup that was right before React SSR was

    01:57 kind of stable. So, we built our own thing. If we had done it six months later, we probably would have gone with React. And then two years down the road, we decided that maintaining your own in-house framework was awful. So, in 2018, we actually did a little comparison

    02:14 of React and Vue, and the team chose Vue. So, we built then a Vue SSR stack that I was running for four years, I guess. And then I joined Remix in 2022, I think. Awesome. So, you worked at Urban Outfitters, working on e-commerce for seven years, and then you're like, sweet, I'm going to go off and work on something else. And now you're

    02:34 at Shopify. Yeah, basically. Well, what's funny is we built this whole Vue SSR architecture. Nuxt was also V0 when we started, so we did a manual Vue SSR setup. And we built all the things

    02:50 you need for data loading and nested routes and all that. And then when Remix officially went open source, and I played around with it, because I'd seen you tweeting about it for a long time, and it was like, oh, this thing sounds kind of cool. I looked at it, and every single abstraction, I was like, I like Remix's solution better than what

    03:06 I've been architecting over here for a while. It was like, data loading, I like what Remix did. Sessions, I like what Remix did. So, then it was just like, I just want to work on that. I want to do that stuff. I don't want to try to build this thing to be as good as that.

    03:22 Yeah, you know, I've had a similar experience. When I was at PayPal, I built a library called

    03:34 Glamorous that was built on top of a CSS and JS library called Glamour by Sunil Pai. And as I was working on that, one of the contributors to Glamorous decided he wanted to revamp the

    03:49 whole thing, and he made Emotion. And that went super well, and I remember there was a moment that I was like, man, Emotion's got this and that and this, that I would have to do a bunch of work to get that in Glamorous. Huh. And so, I just deprecated Glamorous. Like, everybody, go and use Emotion, it's better.

    04:06 Yeah, go do this thing. I was fully prepared. I had never written React professionally when I reached out to Michael for the Remix job. I'd been doing web for a long time, and Remix really hit on all the web primitives that you used to do in the PHP days and all that.

    04:22 But I was fully prepared to be like, oh, this Remix thing didn't work out, and then I was going to just start implementing the Remix patterns in our Vue app. So, I'd be like, hey, we're going to start loading our data like this, and we're going to start doing all this other stuff Remix does. And then it did work out. And it was like, so now I've

    04:40 been telling the devs there, I'm like, you guys should start doing this stuff, and here's how you could start using loaders and actions and things like that. Yeah, yeah. The principles are not exclusive to Remix, certainly. There's been a lot of cross-pollination across different frameworks. And now, what's very exciting to me is that

    04:59 lots of these ideas are getting swallowed up by the underlying framework of React itself. And so, all of the meta-frameworks on top of React can benefit. What I think is kind of interesting, you know, history always plays out differently from what you expect,

    05:16 but you were working in Vue, and that wasn't a problem for me, hiring you, because first of all, we don't hire developers who are specific to a framework. I could tell that you could

    05:33 get around in whatever we put you in. But also, we were kind of planning on making a Vue version of Remix at the time, and it's interesting how history's played out where

    05:46 that's become less necessary. Do you still have hopes that one day the work you've put into Remix Router and making it generic will benefit other frameworks? I don't think it's ever going to get used directly, but like you said, I think it already

    06:03 is benefiting. I think we're seeing these patterns, like you said, show up in other frameworks and other libraries. But I think one of the biggest things we got out of that is it really simplified the whole kind of internal architecture when you just got it

    06:19 to this, like, this thing is just a router, and it does data loading and mutations, but it doesn't have to think about all of the other stuff at the layer above it, right? It's just like in that little part of the stack beneath the UI before you talk to the server. But what I was surprised by is when it went out and I pushed out the Vue version

    06:38 and put it out there, people weren't jumping to be like, oh, let's do this with Vue. It was like, well, I have Nuxt and Vue Router. What is this going to get me? And it was like, well, there's some patterns there, but Vue Router is adding some of this stuff for data

    06:53 loading, and they're adding, I think I've seen, like, RFCs for mutation APIs and stuff. So if anything, I think the inspiration is there. But it's like, we're not going to go out and build this Vue layer thing and try to compete with Nuxt. Like, Nuxt is awesome.

    07:09 You're not going to, like, dethrone someone over there. So I don't think I'm surprised that nothing really happened with that, but it was neat that we got a Preact PR on top of that to do a Preact version that I think Jacob worked on. We got a Svelte version that

    07:25 I'm forgetting his name at the moment, but somebody did a Svelte version and presented it at one of the Svelte meetups. So, like, there were community folks coming in and making these packages. They just never really, like, went anywhere, because all of these libraries

    07:39 have pretty good routers in their own, right? Yeah, I do like what you said about how it helped remix itself by, like, extracting that. I think that what's interesting is I find

    07:56 constraints like saying, oh, we're not going to rely directly on React. We're just going to do this as a vanilla thing. That kind of a constraint sometimes improves the final output in interesting ways. So, like, another constraint that I've noticed the Remix team

    08:15 has on yourself is, let's design this in such a way that you could do this without a build tool. Like, that this does not need some sort of compiler or something. Now, like, we are going to have a bundler, because there's optimizations there, but if we can have it

    08:31 designed in such a way that that's not required, that's a really great goal, I think. Yeah. Yeah. Having to rely on, like, a whole different set of complexity to focus on, like, the thing you're working on is tricky. I love when it's like, okay, here's someone who opened

    08:48 a Remix bug, and it'll be some really complicated Remix setup and all these different route files and everything, and you can go over and try to reproduce that in 20 lines of little JSON routes and components in the router itself. Actually, when it comes to the component side, like,

    09:04 the router doesn't know what a component is, right? So, you can reproduce these issues with just these simple routes that have no UI. They just have a loader and an action and a fetcher. And so, you can, like, really hone in on, like, the details of what's the underlying bug, and you can just fix it without thinking about all of the complexity of, like,

    09:22 here's Remix, and there's a server, and there's an HTTP round. Like, that stuff can kind of be, like, it's not even stubbed. It's just not there when you're thinking about, like, core routing issues. Yeah. I like that a lot. Where do you think the line is, though? Because, like, sometimes

    09:37 having vertical integration can be really beneficial. Like, I guess that's, maybe that's the sort of thing you just kind of develop an intuition for, or do you have any, like, rule of thumb of when should we, like, where's the line between what goes in the Remix router and what

    09:53 goes in the React router? So, I think, well, so there, what are you differentiating them as? Yeah, so, like, specifically Remix router being free of any, like, kind of framework agnostic router versus React router being, like, the React bindings, I guess. Yeah, yeah. So, we've,

    10:14 at this point, the router used to hold everything. Like, the agnostic Remix router managed all of the state, and right now a couple things have been lifted up, and most of the time it's, like, we can't do this without it because either it needs, like, a DOM API. So, view

    10:30 transitions is a good example of something that, like, we pass through a little bit of information, so, you know, like, the link unstable view transition flag. So, you can opt into browser view transitions on a React router navigation, but the router itself has nothing to do with that

    10:46 because it doesn't know anything about the UI. It just has to kind of proxy that information along, like, hey, they want to link to this path, and when you get there, I want you to do it in a view transition, but it just basically sends a Boolean all the way through, and then it's the React layer that has to actually call, like, document.start view transition because you're in

    11:05 React router DOM at that point, and you have a DOM, like, the router doesn't have anything to do with that. So, some of them are really clear because, like, if it's a DOM API, it happens there. If it's React API, like, flush sync, same exact thing. We pass through a single Boolean that just says, like, hey, when you eventually commit this navigation, do it in a flush sync. So,

    11:25 there's stuff like that where the router's kind of, like, I don't know or care what this flag does. The other thing that lifted up is, like, fetcher data. So, with some of, like, the v7 fetcher persist stuff, we actually have some of that data being managed by React now rather than the router

    11:42 itself. So, there's things that you actually want managed by React there for state reasons. Yeah. Is that for transitions then? Because... Yeah. Yeah. Yep. That makes lots of sense. There is a... We wanted fetchers also to, like, you want them to be able to persist beyond when the fetcher actually unmounts. Oh, right.

    12:02 So, we're actually, like, managing some of that state higher up in the React tree so that you can, like, have a fetcher in a modal and you can take the modal component away, but that fetcher hangs around until it actually finishes whatever network call it was making.

    12:15 Yeah. Yep. That makes sense. So, you were talking about, like, DOM stuff, and it made me think about the link component. And I wonder, like, there are some things that the link component does that an

    12:33 anchor doesn't, like prefetching and things like that. But for simple links, I heard rumblings that I would one day be able to just do an anchor tag and just render that. Is that still, like... There are rumblings. Okay.

    12:50 Yeah. I think... Yeah. So, we talked about that when we were... I think when we were sort of figuring out, like, how's RSC going to fit into this remix story and, like, where does, like, remix end and React begin? And, you know, we went down lots of different paths of, like, RSC is

    13:06 kind of sending, you know, a form of HTML over the wire. Like, what if we sent normal HTML? Because you can, in remix today, you could just render the string in a loader and send it back. And you can kind of do, like, you know, here's, like, poor man's RSC. I can render on the server

    13:21 and that component never goes up. We started to think about that kind of stuff where, like, okay, well, if I render a link, it's just going to be an A tag. And then I send that A tag up. How do I, like, hydrate it into a link? So, there's always been, like, reasons where that would help.

    13:37 But I think, you know, that kind of RSC stuff is starting to, like, push it that a little bit. And it's like, hey, like, these are just anchors. And if I'm rendering markdown and sending that up, we have little, like, React hooks that we use on, I think, like, the remix and React router docs websites that have to go through and, like, you know, find all the links and add click listeners

    13:57 or delegate them up to the root. So, there's been sort of, like, I think this, like, thing poking at us for a while. And it's like, hey, like, this would solve some of these problems. This would be, like, a pretty good simplification. So, I think it's possible in V7 or beyond

    14:13 that we would add that. If you look close enough at the remix Fog of War implementation, you'll start to see the first hint of that. I don't know if you've used the Fog of War stuff. Not yet. No. So, if you opt into Fog of War in remix, you'll start seeing these new little data discover

    14:33 attributes pop up on your links. And that's how remix can know, here's all the links currently on my page that I should go and, like, prefetch and expand my little Fog of War route tree. And we initially were like, okay, you could do this with React context, right? Every time a link is rendered, it could, you know, send a bit of data up to some global context and be like, hey,

    14:53 I'm a new link, I'm rendered, you should prefetch me to the route tree. But the same thing, like, if you, links that were rendered on the server from Markdown couldn't participate in that. Right. If you were rendering some big chunk of something in RSC, you would have to then put a client link

    15:09 component inside there in order for that link component then on the client to actually hydrate and, like, talk to the context. So, there were just a couple things where it was like, this would work, like, the context approach would work fine, but it then puts these constraints on an RSC

    15:25 landscape where you now have to have these, like, hydrated client components to participate. So, we chose for that specific little bit of data, like, why don't we just put it on the link? And then we can just use mutation observers to look for changes in the DOM and be like, hey, I found a whole new batch of links. I'm going to go prefetch them on each navigation.

    15:43 Yeah, that's very interesting. So, it sounds like the link component isn't going away because you want to have nice props for different features and things like that and type checking all that.

    15:56 But the primary area where that kind of is annoying is inside of Markdown or some content that comes from CMS or something. And so, in the future, React router will have support for just render a link and we'll make it work with the router.

    16:14 I think so, yeah. We have that, like, there's a very easy event delegation hook you can put in your app today to support that. So, I think that's most likely where I would see us landing is, like, that hook will just be a built-in thing. So, that if you decide there's a portion of your

    16:31 app that can't use link for some reason, like, that's fine. You can just put an a tag and maybe we'll look for a certain attribute on that. There have to be some kind of a way to say, like, hey, no, no, like, some of these are and are not React router links, like, hydrate them or don't. So, there will have to be something on those to opt out or opt in. But yeah, I think

    16:50 there's no reason that that type of a hook shouldn't be first-class API. What we do today is we just, like, we just point people to the React router website code base. We're just like, hey, there's a hook over here. Go copy and paste that into your app. You do that enough times,

    17:06 and you're like, this should probably be built-in. Yeah. You know, though, like, doing that just go copy and paste this approach is great because it helps people, like, validate that this is a good

    17:21 idea and they can make modifications to it. And I'm thinking about the scroll restoration stuff the library that I have. Also, like, let the eco... Actually, this is what the React team does. Like, let the ecosystem figure out a good way to do this. And if there's something that's, like,

    17:40 general enough that's useful for everybody, then we'll build in some primitives that make that easier to do or handled for you. Yeah. Yeah. You can't, like, as a library... And I think this has been, like, really interesting for me personally because I've never worked in open source. I've

    17:59 never maintained a large-scale open source library. So, like, this type of, you know, what feature should we add and what should we not add is a really interesting point for me to, like, deal with these past couple years. Normally, if you're working for a product and you've got a team of 10 developers, you sit down and you're like, we want to add this API. And you can just, like,

    18:19 decide then and there in a meeting, like, that's a good API or a bad API. We want it or we don't. When you've got, you know, thousands or millions of apps using your code, you can't say yes to every little thing, right? You have to. There's some kind of, like, you know, hurdle of, like, yes, this has gained enough, you know, mass adoption. There's enough people

    18:38 that need this feature that it should become first party, first class. And I think that's been really... I had a tendency early on, I think, to be like, yeah, somebody filed a feature request. Let's add that. And then, like, you think of it, the thing just bloats and bloats and bloats if you add every single feature that comes along. So, that's been a really interesting thing for

    18:56 me to witness, like, coming into open source from sort of internal product side. Yeah, yeah, that is interesting. So, are there any things that you've been working on that you're really, really excited about coming in the next couple of months?

    19:15 Yeah. What would I pick? So, I was, I mean, Single Fetch and Fog of War were my two most recent. Yeah, let's talk about those a little bit. Some people might not know what you are referring to when you say Fog of War in particular. What is that all about? Yeah, we had to rename that because the name can be potentially scary, right? Why is my router

    19:34 talking about war? Yeah, yeah. So, it's now been renamed to Lazy Route Discovery in the code. It's still, you know, colloquially Fog of War. But yeah, the idea there is that you, right now,

    19:49 the way Remix works and the way React Router 6.4 works is that we have to know your entire route tree up front so that when I'm going to, like, click on a link, I can immediately decide, okay, this link matches these four routes and I can call those four loaders in parallel. So, Remix made

    20:06 these sort of decisions to have this static route tree fully loaded up front in the beginning. I guess at v1, there was actually a version prior to v1 that didn't do that. But the idea is essentially if you want to flatten that network waterfall, you need to know the route tree.

    20:21 So, that's fine for most apps if you've got 50 routes or 100 routes, like, sending that manifest up. It's not the implementations of those routes. We try to differentiate between, like, what I call the route definition and the route implementation. So, the definition is, like, what URL does this

    20:37 match to? Does it have a loader? Does it have an action? The implementation is, you know, what's the full component that I'm going to render, the implementation of the loader, all that kind of stuff. But you have to know the route definition so that you can match those up front without first reaching out to some server, getting back and saying, okay, now I know what the routes are. Now

    20:56 I can call the loaders, right? Now you have this waterfall hop. So, Remix decided up front we're going to send the full thing. And when you get to thousands of routes, so something like the Shopify.com site was one of the ones that pushed us a bit here. They were like, hey, this is, like,

    21:10 a huge manifest. Like, something, like, it gzips really well. So, it was, like, 100k sending over the wire. But it exploded client-side to, like, an 8 or 10 meg manifest. No way. What? Part of it is just that, like, the way they're localizing isn't ideal. Remix is actually lacking

    21:30 almost a URL rewrite way so that you can say, okay, like, I have multiple static URL paths that map to the same route module. If you want to have, like, slash EN slash, you know, pricing, and then slash ES and the Spanish word for pricing, and you want them to run the same route,

    21:48 Remix actually doesn't have a great solution for that right now. Yeah, that makes sense. If you've got 15 locales, you duplicate every route 15 times. And then if you've got 100 routes, you now have, like, what, 1,500 routes? Yeah, that explodes. Yeah. So, it gets pretty big. And then the other side of it, I think, do you think is that

    22:07 if you have 1,500 routes, like, your users are never going to hit them all, right? Any given user session is going to hit probably just one locale in there. So, that already trims it down by, like, one-fifteenth. But then they're just going to go to, like, the portion of the site that they care about for that session. They're either, like, sticking around in settings,

    22:25 so they're over in billing, or they're over on, like, the marketing page. So, the concept of Fog of War, of this lazy route discovery, is that we're just going to send up the portion of the route tree that you're currently at when you start your session, and then every time you click a link, we're just going to slowly grow that out. And we're going to, like, as a video game map would look like, we're going to see the fog clear, and we're

    22:46 going to discover more of, like, the map as you're going around. Yeah, okay. So, with this enabled, when I land on the page, I am going to have a manifest, like an initial manifest, to get things started. Is that filled with just the routes that are,

    23:03 like, linked to on that page? It starts with just the server-rendered routes. So, if you go to, like, you know, kensydots.com, depending on your layouts, you're going to get the root route and your index route, and then any, like, pathless layouts in between. Mm-hmm. It'll start with two, and then right when we hydrate, it's then going to do that next step

    23:22 of, like, all right, what links are on this current page? And it's going to make one call back to the server, to this, like, sort of internal manifest URL, and it's going to say, hey, I've got these 15 links from this page. Send me back all of the manifest entries that are required to fulfill all 15 of those paths. So, they get patched in, and then you click a link,

    23:41 you navigate to the next page, it collects all of the new links that it hasn't discovered yet, reaches out to the server, and one more batch call for those. So, it's, like, a step-by-step, but generally, it should almost always discover those links before you can click them, right? Users don't click links in, you know, 50 milliseconds. Right.

    24:00 But we should be able to do that link discovery in 50 milliseconds. So, when you get to the new page, we fill them in, hopefully, before you can click them. Yeah, and even if the manifest hasn't been filled in in time for some reason, it just falls back to a regular full-page refresh, right? Like, it's not like it breaks, it's just suboptimal.

    24:19 No, yes, it's suboptimal, but it's better than that. It's not a full-page refresh, it's just that small waterfall. Oh, right, yeah. So, if you click a link that hasn't been patched in yet, the first thing we do is go to the remix server and say, like, hey, I don't know the matches for this link. Remix server gives them

    24:34 back, we patch them in, and then we call the loaders. So, it keeps you in that SPA navigation, but it just adds, like, a tiny little discovery step before the loaders run. Yeah, that makes tons of sense. That's very cool. It's just, yeah, it degrades a little bit, but it should never break. Yeah. That's not what we're counting on. It's a pretty bad feature if it just broke.

    24:57 Yeah. So, what about single fetch? That one, I'll tell you, for me, one of the things I liked about the nested routing with loaders and each one of them being its own URL was that, like, oh, I can cache this one different from this one. And not that I ever

    25:14 actually did that, but it seemed like a good thing. And so, yeah, I was a little bit surprised about the single fetch. And so, yeah, I'd love to hear what were the motivations and how does this make things better for us? Yeah. So, we've talked about this a lot

    25:33 internally, right? Because it's a pretty big change, and I think there was definitely a lot to convince ourselves it was the right thing. But the way I've come to see it after all of our back and forth and how should it behave and all that is that we're simplifying the behavior

    25:51 of Remix for the majority of users. And with that is coming a slight de-optimization in performance that you can opt back into in the more advanced cases. So, what that means is the simplifying is a couple things. One, the headers function has always been a point of confusion for new

    26:11 companies. It's super annoying. Yeah. If you're in a loader and you just return a response, those headers on a data request get used directly because every loader has its own HTTP request. But on the document request, you need this thing to merge them because nested routes exist. So, folks have always

    26:28 kind of gotten confused. And we've seen a lot of people say, like, who've gone to adopt single fetch and they're like, oh, this is annoying because now I have to go either use the response stub that we shipped and then pull back because it wasn't actually any clearer. They realized at

    26:46 that point that they had never been sending headers on their document requests from the get-go because most of the time you do your debugging through client-side navigations. So, you're looking at the network tab and you're looking at the loader request and you're like, oh, cool, there's my header that I put on my loader. But folks don't hit the document and look at

    27:05 the headers that come back. So, we found a lot of people were just missing this completely. And it's really weird to have two totally different paradigms there for how you set headers. So, that simplifies it, one. And then the other one is just the caching,

    27:21 is that either you can cache your document and everything involved with it, or you can't. So, you could have caching on the individual loader, but then you can't cache the document because the document's got all of them. So, there's a caching story that was different for client-side versus document requests. So, there was just all these things you had to think

    27:41 about. So, we think that it gets a lot simpler there. And we think that the cache hits should go way up because the URLs stabilize a lot more now. We don't have query params on there by default. It's just like, give me slash path dot data. And that works. You don't have all the different

    27:57 combinations of the query params, like the routes param on the end. So, basically, the cache enumeration should reduce, and you should get better cache hits until you start opting into all of these little performance optimizations. And so, that is, to your point, you like the

    28:16 individual caching on one endpoint, you were saying, and that's still going to be possible. You'll just opt into that either by putting a should revalidate on a route. By putting that on there, you can get into fine-grained revalidation that way. So, instead of opting into recalling a

    28:35 route, should revalidate is going to kind of reverse. And now, you'll opt out of it being recalled on like a normal navigation. And then the second one is adding a client loader. So, as soon as you add a client loader, we can't just blindly make a call to the server and say, hey, give me all the loaders. Because in the client loader, if you don't actually call your

    28:54 server loader, we don't want that data from the server. So, we can't run those server loaders. If a client loader exists, we have to kind of defer to you and say, hey, if you decide to call the server, then I'll make a singular call for just that loader. So, you can get back to the

    29:10 remix today behavior by just adding a client loader to a route that just passes right through to your server loader. To me, it's changing these defaults to be simpler and more cohesive across the document and spot navigation story. And then it's giving you the ability to get back to what

    29:29 Remix does today for the more advanced users who want to take on that complexity in their head and in their app. Yeah, I like that. Okay, I feel a lot better about that. And then the other cool thing that that enables now too is no longer needing JSON or defer. You just return the object

    29:46 itself. You can have nested promises, which would be super nice. Yeah, that all makes a lot of sense. And we could have, to be fair, we could have done that without single fetch, right? We could have changed, we could have used TurboStream in the old paradigm too. Another thing we talked about,

    30:01 like what's exciting things for me personally coming up, is that it unlocks full pre-rendering too. So that was another thing we couldn't really do quite right before. So now with the old approach, like URLs or these data requests were driven by the query params. Oh, right.

    30:18 Now that we've gone to like a dot data, we can actually pre-render out all of your data and store them off on a CDN next to your pre-rendered HTML pages. And you can start client-side navigating around your fully SSG'd app if you want. And you can pre-render that stuff out of

    30:34 build time. So it's not my, you know, I still like SSRing apps, but so many folks have asked for SSG since the birth of Remix. And it's like, okay, now we can do it the right way. You could kind of half do SSG before, but with this dot data param, we can write out files to a system

    30:53 and actually do SSG there. Yeah, that is cool. You mentioned a couple times that that request for loader data no longer has search params. So can you tell me how the implementation works so that I can still reference those inside of my loader?

    31:12 Reference which? Reference search params. So let's say I'm building a user search page. It'll have your app search params. Okay, it just won't have, got it. Not the like the underscore data. The implementation detail ones are basically gone for the default case. Yeah,

    31:31 very good. We'll proxy along the app ones. Those are important. Yeah, awesome. Hey, Matt, this has been super cool to chat with you about what you're working on with Remix and React Router and everything. Was there anything that we didn't get to talk about that you were really hoping we would?

    31:49 No, I think, like I said, we ended up, the big React Router 7 thing I'm psyched about is the pre-rendering. I think there's a lot of really cool stuff folks can do in, you know, beyond just normal SSG. So when you bring together React Routers, pre-rendering, the concept of single

    32:06 fetch and being able to render out that loader data at build time, the client data, the client loader and hydrate fallback APIs that are in Remix today, you can get this really cool architecture of like, I want to pre-render out portions of my pages down to the hydrate fallback.

    32:25 And then I'm going to serve them up and it's going to pick up at that spot in the route tree and call client loaders the rest of the way down. So I'm personally really excited to see the types of patterns that folks get to there with like, you know, I don't want to call it partial pre-rendering that would be confusing, but you can. You can partially render a portion of your

    32:45 route tree at build time with the pre-render feature, send that over to your CDN, and then you can pick up as a spot, you know, at runtime. So I think there's been some really neat patterns folks are going to discover with like the combination of those three things. Yeah, that is very interesting.

    33:03 I have like, I have a conference talk like kicking around in my brain that I need to flesh out at some point because I don't think it's obvious right now the things you can do with these APIs when they all kind of land together. Yeah, yeah, I think that's cool. That's exciting.

    33:20 What's interesting, I kind of assumed that I would, the older I would get, the less excited I would be about this job. But that is not played out. Like things keep happening. React 19 is just going to be so killer, so amazing. And React Router v7 is going to be awesome. Actually,

    33:39 we're a little bit over what the time I originally planned, but I do want to know what your current thoughts are on adapting React Router and Fetchers with the new forms, like primitives, the actions that React 19 has.

    33:58 I don't have deep thoughts there because I, to be totally honest, I don't know the React stuff yet as well as I should. I've not, like Jacob and Ryan have gone really deep in that stuff. I haven't yet. So that's definitely that I need to dig in and figure out. But I know right now

    34:16 our general consensus is that the React stuff is really cool. It's not quite what Fetchers are today. We don't quite have the same level of granularity and stuff I think you can do with Fetchers. So I think right now the idea is we want React Router v7, when React 19 comes out,

    34:34 we want you to be able to use all this stuff, but we also still are going to keep Fetchers around until we think that you can do everything with the React APIs that you can do today with Fetchers. And then at that point, I think we would be more than happy to say, all right, Fetchers,

    34:48 be gone. Use the React stuff. But we think there's some stuff that has to come along there still. Yeah, sure. And even in that theoretical future world where Fetchers are gone, the Remix team has demonstrated time and time again that you all care about migrations and

    35:07 making that smooth. And so yeah, even if today we're like, oh yeah, actions are going to be amazing. It's not like React Router v7 would say, oh, one more breaking change. Let's get rid of use Fetcher. We've already talked about we don't have it today. I actually started working on it a

    35:25 little ways back and I think we kind of put it on the shelf until we could get through this React Router v7 stuff. But we've talked about inline actions for Fetchers. It's a little bit annoying right now that you have to go create a route and put a loader on it. It'd be nice to just put the

    35:39 function right there. And that starts to feel a lot like server actions or just actions in React. So yeah, I think it's absolutely possible. That bridge is there that we can say, okay, if we ever decide Fetchers are going away, we can start to slowly transform Fetchers to look and

    35:56 feel a bit like native React so that you can little by little move your stuff over. We're definitely not going to just yank it out from under folks. Yeah, yeah. I love that. Love that about you all. Thank you. Matt, what's the best place for people to keep up with what you're doing?

    36:10 Twitter and GitHub. It's at brofdog11 to D-A-W-G. That is my middle school instant messenger name, which I just stuck with forever. Yeah. Hey, good for you for sticking to it. I left my junior high

    36:29 usernames behind me. Yeah. I think it was when GitHub first came out. It was when all of this stuff started to come out, right? Twitter and GitHub and all these things you needed a handle for. I was just like, what handle? I was like, I used to have a handle for instant messenger. I

    36:45 guess I'll just stick with that. I had no idea at whatever age that would have been for me, age 26 or something, that I'd be deep into my career and I'd still be known as brofdog. But I'm good with

    37:02 it. Yeah. It's good. That is great. Hey, thanks again, Matt. This has been awesome. We'll see everybody around the internet. Yeah. Great, Kent. Thank you.