951: A First Look at Remix 3

## Metadata
- Author: [[Syntax - Tasty Web Development Treats]]
- Full Title: 951: A First Look at Remix 3
- Category: #podcasts
- URL: https://share.snipd.com/episode/454e383e-8de9-45a8-a626-d05b218fc77e
## Document Notes
## Highlights
- **Standards-First Framework**
- Remix 3 embraces built-in web platform primitives like Events, Signals, Streams, Fetch, Request and Response.
- That standards-first approach reduces custom API surface and improves portability across runtimes.
Transcript:
Scott Tolinski
Than that. So first of all, Remix 3 uses the platform, meaning that all of the features of Remix are things that are built into the web platform. Namely, events is probably the biggest one that we have here. Signals, which we'll talk about that. The backend, web streams, web requests, web response, fetch, state, all of the stuff is built with browser-based, or not even browser-based, JavaScript native features, which I Am a huge fan of. Like little abstractions on top of vanilla in the past, but they've never been like amazing. It's usually just because I'm building an app and I need a little bit of extra stuff on top. But this is just like fully thought out. So Remix 3, it's full stack. And I mean like fully full stack. It's got a whole component model built in, has a router built in. They're building an entire, uh, component library, uh, which, yeah, something similar to like, like a shad CN or something where they have a whole designer on staff. So they're actually going to look good. It's all based on HTML, CSS. Obviously they have a full server side to it as well. Mutations partial rendering of different parts a lot of handy helpers for things that you commonly want to do like stream in files it's all llm ready which is something that i predicted Meaning that a lot of the features like i'm just this morning i was just through, I had to literally take the demo off of the CD for me to use it. That's fun. A lot of the features are just like, they're just, it's just vanilla JavaScript at the end of the day. So an LLM is very easily able to generate code that looks like this because it's not a lot of custom APIs. ([Time 0:03:21](https://share.snipd.com/snip/7cefb0ef-300a-4a1d-95a8-efbf517acda9))
- **Explicit Render Model**
- Components use JSX/TSX and state is plain variables, not automatic reactive values.
- Re-rendering is explicit via this.render and template code runs separately from one-time setup code.
Transcript:
Scott Tolinski
And I was really happy to see that because what that means is that, first of all, I'm comfortable in JSX. I'm just going to say JSX. And when I say that, I mean, obviously, using TypeScript as TSX. But then all of your tools that use TypeScript or TSX or JSX, they will just automatically work. And so all of your linters, all of your formatters, all of your editor tools, those things just work. AI is really good at writing JSX at the end of the day. So there's no like new custom templating language that you're going to need to learn. State is simply just variables. Get this. If you want to store some data in a component, you simply just create some variables, and then those variables are then scoped to the component itself. And then probably the biggest... Do they update reactively? Sorry. Kerfuffle. That's it. It is not reactive. That was the biggest kerfuffle. So if you have a variable and you throw it into your JSX somewhere, that will not reactively update. You have to explicitly call this.render when you want your component to re-render. And the sort of the thinking behind that is that React gets into these weird loops of things, or you end up re-rendering too often, or you have something way up higher in a context tree, And then you want to access that lower via context, and you have to re-render the whole tree all the way down. So this gets around that by nothing is reactive. You simply just, when you want to explicitly render, you call this.render. I want to get your thoughts on that before we dive into a little bit more of that. What are your thoughts immediately?
Wes Bos
My thoughts are that it would be nice if that was the api but then there was also a reactive state primitive um you just took my next point out okay because that's how that's how svelte 5 Works again sorry folks i'm gonna say it in this episode but they have a reactive variable primitive. You get a fine grained reactivity where you want it. But then if you're just defining normal variables, they're just variables. Uh, like, yeah, yeah.
Scott Tolinski
So to add reactivity to this, like I was sitting there watching this being like, ah, I don't know if I want to have to call it render every single time something changes. But there's nothing from stopping you from bringing in your own data store and then just listening for changes on that data store and passing it, this dot render, into the callback. So somebody already hooked it up to X state. Or if you're using any of your local first packages, like zero sync or whatever, that's a reactive data store. And then you can listen for all of those things. So what is really cool about that is you don't have to use a specific state and you don't have to use a specific package that adapts to this.
Wes Bos
Yeah.
Scott Tolinski
Just bring your own data store and then just pass it the render method.
Wes Bos
Another question is when render calls, it's rerunning that entire function like React?
Scott Tolinski
Yes. So what I'm showing on screen right here is a, well, it's only rerendering this component. I'm not sure if it will re-render it all the way down the tree but but still like re-running a function right not like oh no it's not yeah it's not it's not that's that was a good question So one of the things was i have this function here and if i put some code at the top of the function, when that whole thing re-renders, then all of that code at the top of the function also Re-renders. And then you get into this weird thing where if you add event listeners in there or something like that, the function doesn't return JSX. It returns a function that returns JSX. So meaning that all of the code inside of your component that is above the return is simply just called once when that initial thing is run. And then the actual templating is rerun when the render is called. ([Time 0:09:29](https://share.snipd.com/snip/61321586-f22d-4fef-bdf8-4f4a2be25511))
- **Bring Your Own Reactivity**
- Bring any reactive store you prefer and call this.render from its change handler to update UI.
- Use existing libraries (xState, local-first stores) instead of forcing a new Remix-only state system.
Transcript:
Scott Tolinski
To add reactivity to this, like I was sitting there watching this being like, ah, I don't know if I want to have to call it render every single time something changes. But there's nothing from stopping you from bringing in your own data store and then just listening for changes on that data store and passing it, this dot render, into the callback. So somebody already hooked it up to X state. Or if you're using any of your local first packages, like zero sync or whatever, that's a reactive data store. And then you can listen for all of those things. So what is really cool about that is you don't have to use a specific state and you don't have to use a specific package that adapts to this.
Wes Bos
Yeah.
Scott Tolinski
Just bring your own data store and then just pass it the render method. ([Time 0:11:56](https://share.snipd.com/snip/e161a38f-8bf2-41e9-b8d3-b2ae76b1bbde))
- **Event‑Driven Interaction**
- Remix centers app logic on custom Events and EventTarget to dispatch and listen for interactions.
- Events bubble, carry payloads, and enable composable, decoupled interaction patterns across components.
Transcript:
Scott Tolinski
Now let's talk about probably the biggest idea here, and that is this whole events-based architecture. So everything in Remix is based on an event, which is a JavaScript primitive, which is a browser primitive, being an event target. So if you think about, let's peel this back. If you want to think about a button or any HTML element in general, right? Those elements, you can listen for events on them, right? You can grab a button. You can listen for a click on that event. That button itself can also dispatch events if they need to be custom, right? You can fire off custom events from an event target, which may or may not be an HTML element. And if you have not done that before, I encourage you to look into dispatching custom events because it's really nice. What you can do is you can make some sort of like data store. And when the data in that data store changes or when anything happens to your application, you can simply just fire off events. And those events will bubble up to all of the parent elements. So in the past, I've made a hockey game. And whenever anybody scores, or whenever the score changes, or whenever the players change, you can just fire off events saying like, the score has been updated, the players have been Changed. And then other parts of your applications that necessarily care about those events happening, you can simply listen for them. And if you don't want them to bubble up even further, you can stop propagation. You can use the event default, prevent default API. So events in JavaScript, and I know a lot of you know this, but events in JavaScript are not just the baked in ones. You can make your own custom events. And those events can also contain payloads of information about what happened, you know, like event.current element or whatever element you click, that's baked into JavaScript. But you can also just fire off a whole bunch of custom data with your event. And anybody who wants to listen to that can.
Wes Bos
Yeah, I love that. I love that. Custom events, man. Yeah, I've been building a handful of utilities myself and custom components and stuff I use on every project. I don't know about you, but you collect these things that you just end up rewriting on every project. I'm like trying to formalize those into a package for myself. And I did it first with my own like personal little CSS framework, and now I'm doing it with standard space Svelte components. But custom events, totally underrated, man. I've been using a lot of custom events, and I love that stuff.
Scott Tolinski
So that's just, so far, all the event stuff I've been talking about, that's just JavaScript, right? So the Remix stuff is based on that. It's built on top of custom events. So what happens is, for example, I'm showing a fetch episode method that I've created. This is a class called podcast player, and it extends event target. This has nothing to do with Remix. This is simply a class that extends event target, i've added a whole bunch of custom methods onto that one of them being fetch episodes and what that does is it hits the syntax api it json And then when that data comes back we're simply adding that episode to state by saying this dot episodes equals right yeah i't even need to spread it. I don't even know why I'm spreading it. That's a reactism. You could just push it. I could just push it. That's how far I've got my brain rotted by react state because I immediately went to spread it rather than just using my mutation push.
Wes Bos
Mutating state is not a terrible thing, folks. No. It's easy. You don't gotta create new state every time. Yeah.
Scott Tolinski
So that state of this.episodes has been mutated, but because it is not reactive, then I have to go ahead and dispatch an event, which is a custom event that I've created called create List updated. And that is a new remix API called create event type, which again, it's just a thin layer on top of dispatch event that allows us to have fully typed things. So that way, when you're firing off events or listening for them, you know exactly what type of data you're getting. Because if you've ever used TypeScript and add event listener, you know, there's all this BS around is type of, you know, you have to check if it's a specific type and it's kind of a pain. So that's the firing off of it. And then you can simply just go ahead in your UI. So the event target code, that's kind of like the logic and the state of what is happening. And then in the actual UI, the component part of your remix application, you simply just listen. This is their own ad event listener syntax here, where you can listen for like something on document or something on player. In my case, I'm listening for a player. And then you listen for specific events like a spacebar event. Or in my case, I'm listening for a list updated event. So when the player fires off a list updated event, then I'm going to go ahead and run this dot render. And what that will do is it will update this component. And of course, you can do other things in this regard as well. ([Time 0:15:15](https://share.snipd.com/snip/aa4a10d1-1328-4c16-ae48-7aa31de17120))
- **Type‑Safe Context Access**
- Context passes objects (like EventTargets) down without triggering automatic re-renders.
- Remix uses this and the this keyword with generics to keep context access type-safe.
Transcript:
Scott Tolinski
Do you have to pass things down? Is it down via props? Can simply just put it on context and then you can access it by this.context And that will not re-trigger, sticking things in context is awesome. I love it. But the only downside is that it sucks for for having to trigger those cascading re-renders um so in this case you just go into a child component which is just a function grab the context And then you can access all of the items that were on your your initial player you can listen for things in here as well. Do exactly what you want.
Wes Bos
Yeah.
Scott Tolinski
And is that type safe? Yes. Yes. It's type safe all the way down. And the reason why they do that is they have to use the keyword this, which a lot of people groaned at initially, myself included. But if you, here we go, this one here, remix dot handle, you pass it a generic of podcast player. And then you can say this dot context, whatever, and you have access to all of your types all the way down. ([Time 0:21:01](https://share.snipd.com/snip/97f804de-d470-4c71-babb-f6b2fc1a1bbc))
- **Signals For Async Cancellation**
- Remix exposes AbortSignal-based 'signals' to cancel async operations like fetches.
- This leverages native platform abort behavior instead of framework-specific cancellation hacks.
Transcript:
Scott Tolinski
Signals. So they said, we have signals, and everybody got really excited. But they said, not that kind of signals. Okay. So when you have an async operation, most likely the most common one is like a fetch request. Sometimes you want to abort those specific things. So in React, you might need to like have some sort of cleanup function or you might need to cancel it or use something else. In Remix, they're all using this idea of abort signals, which is something that's built into the platform. So if a component like maybe fires off a fetch request and then it's unmounted, you can give that abort signal to Remix and it will simply just call it and abort that fetch request if that's What you want. So yeah, again, just another example of using the built-in methods that are built into the browser. Benefits of standard. I wanted to press on this a little bit is that you can bring your own whatever you want and and they're not the first one to do this but like there isn't necessarily a remix way of of doing Most of this stuff so you can bring a library in from it could be a web component they showed many examples with web components you can bring a library for whatever you want, and it's just Going to work. There's no weird oddities where, oh, I got to stick this inside of a useEffect, and I got to memoize it, and I got to have a ref for this thing. You know, like if you want to remember something, maybe you have to stick it in a ref so that when that function reruns, you're not overwriting it. No, it's just variables. It's variables and properties and events all the way down, which means bringing your own whatever. You want to bring your own reactive data store to this. You want to bring xState. You want to code up your own thing. It seems to be that you'll be able to do a lot of this. ([Time 0:24:25](https://share.snipd.com/snip/e44baefb-9aef-4025-b473-c17a83a04d59))
- **Server Uses Web Standard APIs**
- Server APIs use web Request/Response and Web Streams rather than Node-specific APIs.
- That makes code portable across Node, Deno, Bun, and Cloudflare workers without Node baggage.
Transcript:
Scott Tolinski
So all of that was simply just the like the front end UI rendering part of things. But this is a full stack framework. So I think Ryan did a lot of work on like the actual front end side of things. And then Michael, I've been watching his GitHub and he's been cranking out a lot of these like tiny little standards based packages. And this is what it was for. It was the server side is standard web requests and web response, meaning that it's the same code that you would write in a Cloudflare worker. It's the same code that you would write in like a Hano.js. It doesn't use any specific Node.js APIs. So if you're using streams, you're not using Node streams, you're using web streams, which of course works in Node. It works in Dino, it works in Bun, it works in Cloudflare workers, right? It works across the board. So everything that they've done has sort of like gotten rid of any node baggage that maybe you had. ([Time 0:28:34](https://share.snipd.com/snip/69630e2c-28bc-44d2-b8b6-17feaf1bbb52))
- **Frames: Async URL‑Addressable Components**
- Frames are URL-addressable async components that can server/client render and provide fallbacks.
- Each component can have its own URL and loading strategy to avoid blocking the rest of the page.
Transcript:
Scott Tolinski
So they were teasing this idea of like iframes being the way to share code. And I wasn't sure if they were joking or not. And it turns out they were kind of half joking. So in most of these frameworks, like in React and React server components, you have an asynchronous component that may take some amount of time. It's server rendered asynchronous component that maybe it needs to fetch some data and then render out and kick it out, right? There's all this like, and Svelte has this now as well with their sync components where you're not sure, should I wait for this component or should I show like a rendering screen? So it seems to be the same idea is now in Remix with this idea called frames. A frame receives a URL, which is kind of nifty, meaning that every single like a component in your application has a URL and all of the data for that URL can be sent. Sorry, all of the request details can be sent over a URL. And then a frame can be client rendered, can be server rendered, it can have a fallback. So if it's taking a little while, you can show a loader screen or you can decide not to. ([Time 0:31:24](https://share.snipd.com/snip/785105a2-bf96-4273-9d8e-2baa653b793d))
- **Stick With ESM Tooling**
- Build tooling outputs ESM; use esbuild or Vite to produce ESM bundles for server and client.
- Expect server-side on-the-fly compilation in Node via esbuild or prebuild at deploy time.
Transcript:
Scott Tolinski
So I already mentioned it's JSX. It's ESM all the way down. So like they didn't mention anything about tooling in the talk. So I asked them after them. So like, what were you using there? Obviously, you had some bundler running to run this code. And Michael said it's just ESM all the way down. So anything that can output ESM can be used as long as it can bundle it output. So their demos are using ESBuild to just regenerate it. ([Time 0:33:08](https://share.snipd.com/snip/5ec2da96-cbab-4388-abb0-f62c5c847e3a))
- **Explicit Code-Based Routing**
- Routing is code/config-based (named routes) instead of file-system conventions to handle large, complex route sets.
- Named routes and explicit methods reduce implicit magic and improve debuggability.
Transcript:
Scott Tolinski
It's a code-based router, so there's no more file system-based routing. They had some interesting talks from Shopify before in the morning, and Shopify was saying, like, we have thousands of routes, and we don't even know what our routes are. And I think that was to sort of tee up this whole like this is a config based routing there the all of these routes are named meaning that you would you give each one a name and you could each One could have like a get and a post and and whatever so it's all of this stuff is very explicit. Whereas like you probably saw some of the screenshots and were like, oh, you know, I don't like that. And there's there's no magic in any of this. Yeah. Which I like a little bit of magic, but also I like a little bit of explicitness. I find it very easy to debug just being a couple hours in. ([Time 0:34:48](https://share.snipd.com/snip/242c2966-c514-41a1-ad17-66f3da8d0731))
- **Let Community Conventions Emerge**
- Expect to adopt community conventions for missing defaults rather than framework mandates.
- Watch for common patterns to emerge (stores, render helpers) and reuse them for consistency.
Transcript:
Scott Tolinski
So questions or concerns? My only concern is like, will I like the manual re-rendering? And will I like the like being stored on an object? Or will I like something more, which my favorite right now is like what Svelte has, where you just create a data store, import that thing when you want it, export it where you don't want It, you make computed. That's a very good approach to data. And will I like this more? It's yet to be seen. I'm not quite sure. Will the manually re-rendering get annoying? Will people simply just put this.render everywhere? Right. Because they don't know. They're tired, yeah. Like I had a couple examples and I clicked it and nothing happened. And I saw the data log in and I was like, I'm missing a this dot render somewhere you know and like is it better to do something like svelte where svelte will just figure out which parts to
Wes Bos
Update for you you know where it's actually reactive yet to be seen yeah will the community come up with a convention that ends up becoming the way? ([Time 0:35:59](https://share.snipd.com/snip/a2ef6ff8-9b92-4b8b-b9fb-9f426f7ce7f5))
- **Bosch Dishwasher Win**
- Scott replaced a decades-old dishwasher with a Bosch 800 series and praises its cleaning and quick cycles.
- He values reliability and paid more to avoid recurrent repairs and cabinet damage from cheaper brands.
Transcript:
Scott Tolinski
Died after 21 years or 23 years old or something like that it lasted a long long time and it finally uh killed itself and no longer was i can't say that we're gonna get banned unalived itself Yes yeah okay cut all that out it finally died so we bought pretty much the exact same one this is a we got a bosch 800 series and this thing is freaking awesome man it just like scrubs the Dishes so super well there's three rows it feels like somehow there's more space inside of it for all of the kids stuff there's an app which is a bit odd but it's kind of nice because you Can ask google how long is left on the dishwasher cycle. That's pretty sick. It's got the quick 60, which our old one didn't have. We love that feature where it's like, I just need to run like a quick load. And I don't want to wait around for two and a half hours. ([Time 0:41:40](https://share.snipd.com/snip/5b58283f-35ca-45dc-bf31-fa75d1d74eb5))
- **MoCA Coax Backhaul Success**
- Wes set up a MoCA 2.5 adapter to use coax as wired backhaul and saw near-gigabit speeds in distant rooms.
- He avoided costly rewiring and achieved reliable performance for many devices across the house.
Transcript:
Wes Bos
I have one gigabit network these go 2.5 gigabits and over coax and i just like a 1990 house, you best believe there's coax in every single room of this house. They're like, you need, you're gonna need TV in every room. So we, we have for our mesh network, I now have a fully wired backhaul via coax. And I was in the basement, which is like the furthest away worst router um and we have so many things like uh on that router that just like the internet's always chugging and now i got 900
Scott Tolinski
Up and down from that router uh just because the coax backhaul no problem so it works that well hey i always thought like ethernet over like over power or ethernet over coax i'm like yeah I bet it's a pain doesn't work as well. But you're getting full speeds. 2.5.
Wes Bos
Yeah, you know what? I did not trust. The speeds were so good. I did not trust the speed test on the router. I was like, is this running like the full mesh speed test? Or is this running from this specific endpoint? And it was that endpoint. So like, they didn't call it an endpoint, whatever they call it. Whatever that router was. So yeah, man Mocha, sick.
Scott Tolinski
The stuff works. And it's, what was it? Like a hundred and, I'm looking at it right now.
Wes Bos
The one I got is 130 bucks for a, oh no, 119 bucks for a two pack.
Scott Tolinski
Oh, that's cheaper than a box of ethernet core. You would have spent more um running wires yeah ([Time 0:45:17](https://share.snipd.com/snip/50a310a3-f89e-4ed4-8b00-e18dc2584e69))