If you’re frustrated by React, try this

Learn With Jason S8.E13 Jul 3, 2025

At 3kB, Preact promises us a drop-in replacement for React’s developer experience. Maintainer Jovi De Croock teaches us how it works.

Read the transcript

Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.

JASON: Hello, everyone, and welcome to another episode of Learn with Jason, we are going to be digging into an alternative to one of the most popular ways to build websites on the web today and that is React.j. We're going to be looking into Preact, which started as a port. But it's grown into more. Let's jump right into the episode without any further ado. (Music playing) Jovi, thank you so much for joining us. How are you doing?

JOVI: Great, how are you?

JASON: I'm doing amazing, and I realized I didn't check with you before we started. I am pronouncing your name right, is that right?

JOVI: Yep, that is correct.

JASON: I was like, that soul deep panic where you're like, oh my God, I might be doing this wrong. So for folks who haven't seen you on the show before or maybe haven't seen your work elsewhere, can you give us just a quick background on who you are and what you do?

JOVI: Yep, so, hey, I'm Jovi or Jovi, however you like. Funny thing, my mom says it one way and my dad says the other, so both actually correct. I'm from Belgium and I maintain Preact, Signals and in the GraphQL space, as well. You might have seen me about the GraphQL clients, but that's not what we're here for. We're here for Preact, and, well, yeah.

JASON: Well, amazing. Yeah, that episode on urql is great. If you are interested in GraphQL, go ahead and jump back in time and find the older episode, you can search it on the CodeTV site. Let's talk about Preact a little bit. On uh its face, it's an interesting project because it, where a lot of projects, you know, if you look at View or Svelt or whatever are a departure from other frameworks they're being compared to, like React. Preact is kind of has a different origin story. And it's much more tightly coupled to React. Can you talk a little bit about a little bit of the high level of had what is Preact? And why does it exist?

JOVI: Preact when I started was 6 years ago, when we released the X release, the 10 release. And what we really pride ourselves in, because like, the React drop and replacement is like something that grew out of it is that Preact is extremely extensible. Marvin, maintainer, has really cool blog post about Preact options, the secret sauce about Preact. And actually, those option hooks are in the Preact core library. And we use them to create React compatibility, to create hooks, to create signals. So Preact Core in and of itself is just you can do DOM operations and you can do diffing and sprinkle a lot in there if you want. It's like extensibility is our motto.

JASON: OK. Yeah, and so, the things that really stand out about Preact is, you know, one of the things that people will kind of ding React for when they look at it as a framework is you're shipping a lot of JavaScript just to get the framework into the browser. There are a lot of things that sort of feel like working around older decisions the framework made to get basic functionality. I think use effect is a big target of that criticism, where it feels like to work with a third party library instead of doing something that feels like JavaScript, it feels very much like you're managing your framework. And so, Preact is coming with a pretty interesting offer, which is as you said, you can do all of the things that you can do in React. You've got kind of a startling level of compatibility to React to the point where with the Preact compat, you can drop it in and most of the products will work, right?

JOVI: Yeah, we basically have this alias approach where we say add this to your web pack and whatever config. And this React library will just work. There are, of course, exceptions.

JASON: Sure.

JOVI: I like to say we support 90 to 95% of the ecosystem.

JASON: Which is

JOVI: Baseline.

JASON: Well, and what I think is impressive about that, when you look at what you're saying, you're saying 90 to 95% of the React ecosystem is supported with compatibility mode. And you are shipping what? 3 kilobytes of JavaScript for framework Core?

JOVI: So the Core, we rebranded for V11 to 4 kilo bytes just to give

JASON: Get it. Get it.

JOVI: So the Core will be 4Kb, compressed. If you add compat, that's another 4Kb extra. Hooks and all of the compatibility things. We'll look at a few in a bit. But yeah. There's a lot of things you need to alias to get around certain things that React does, like, for exampler, on change in React, is on input in the browser.

JASON: Mm hmm. When we talk about this, though, that's still, what 8 to 9kb?

JOVI: Yeah.

JASON: Fully compatible React framework. React and React DOM when Jziped is 31.8 kilobytes.

JOVI: I think so nowadays, I'll quickly use bundle JS for this. I think nowadays, it's like 50.

JASON: 50. So somewhere between 4 to 5 times smaller to use Preact and you get 90 to 95% of the ecosystem support. And I imagine that the 90 to 95% of the ecosystem support is going to include the big things. You can use React router, a lot of the the things that most projects are going to be reaching for. You know, if you want to pull in Core or React things, is that a safe assumption?

JOVI: That's a safe assumption. The exception in that being Relics has some components that go internal, or used to. I haven't checked lately. But we have had a few issues with.

JASON: Nice. OK, cool. And so, the thing that's exciting about this, is that you are able to more or less use all of the knowledge that you have if you've been a React engineer for a long time. But you get this sort of immediate level up of being able to ship less code to the browser. And then, the other thing that you get is what you're talking about earlier, you said you're the maintainer of signals. And so, a big thing that a lot of folks have been excited about in Frameworks, I think the buzz really seemed to pick up with Solid, and I know it predates, Solid, as well, but it picked up in every framework except React now has some instance of signal shipped in the framework. Can you talk a little bit about what that means and why everyone's excited?

JOVI: Yeah, Ryan's done a good job of exciting people. He does great explanations. So, for those who don't know, with Signals, the concept that Ryan Corneado with Solids preaches, you can achieve very fine grade reactivity. You update the signal value and the text note in the browser update without having to rerun the components to know the results. Signals and Preact try to take the preroute, you'll have some upgrades. We can do fine grain updates on text notes and DOM attributes, but if you use a signal in the component closure, we'll fall back to the virtual DOM. So you have this middle ground solution.

JASON: Got it. Yeah. I mean, this is exciting stuff. It's sort of, it's sort of I think the challenge that a lot of folks have is, you know, you don't necessarily want to throw the baby out with the bath water. And I know that I'm somebody who has been growing increasingly frustrated with React over the last little bit because it sort of feels like they're trying to force me to write code in a way I didn't want to write code. I picked up React because I like this component based, browser based way of writing. And it feels like the framework is sort of pushing me back toward do it like PHP, we want server components and these things, and I know there are valid reasons for the architecture, but I'm not Facebook, I don't have these problems and I'm had not Vercel. It's been frustrating to sort of get told that the way I like to build using this framework is incorrect. But I also don't necessarily want to throw out all of my experience with React and all of the things I've learned about how to use it because every time I pick up View or Svelt, I enjoy them a lot, but I don't have the muscle memory. I have React muscle memory. I've been writing it for a really long time and I sort of just know how to do things in React. So I don't to lose that experience. I want to move quickly. And Preact is really, really fascinating for that approach. It gives me the ability to keep all of this muscle memory I have. And also, not to have to buy into this sort of push toward the server that we're seeing in React. Which that's just kind of me up on my soap box here. [ Laughter ]

JOVI: To play into that, this is not acknowledged by any of the other maintainers, but I've jokingly said that our catch phrase should be "bring your React A team knowledge and have a good time." But yeah, that's just me.

JASON: Yes, exactly. [ Laughter ] I mean, it's yeah, and you know, no shade on the people who are trying to build things they think are important. I think it is important to push the web forward and to build the things you think are useful. It's just diverged from what I think is useful. And so, having an option to keep my experience, but not have to introduce these concepts that I'm not necessarily in need of has been very good for me. So, with that in mind, is there anything else high level that we should talk about regarding Preact before we just dive in and get our hands dirty?

JOVI: I guess the only big change, which can segue into us going to the dogs, is Preact can be done in multiple ways. You're not stuck to a bundler. You can go the ESM route and use our htm library, and not even use JSX, like we have multiple ways to build. Which

JASON: Right.

JOVI: I find very fun.

JASON: Yeah, and I know you've got the webpack plugin. And if I'm using Vite, the same way with View or Svelt or anything else.

JOVI: Exactly. Or even without a bundler.

JASON: That is nice you can bring it in and ship it in se a CodePen. That's pretty cool. A lot of times, that does prevent people from being able to post a little quick experiment is that you can't. You've got to I don't want to necessarily ship it. Sometimes, I want to show somebody something cool. But with that in mind, let's go figure out how we can actually do this ourselves. So I'm going to share my screen here. Make sure it's the right one. And get us into the view that I want I wanted to be in, which wasn't that one. It's this one. Let's talk for a quick second before we get going. This episode like every episode is being live captioned, we've got Diane from White Coat Captioning taking down these words if you need assistance understanding what the heck is coming out of mouth, because I'm a mum BLER, go to LWJS/captions. If you or your company want to help make it more accessible to watch Learn with Jason or CodeTV make more stuff, go to CodeTV.dev/support for more details. With that being said, we're talking to Jovi today. So here is the your portfolio. So we'll throw this here. And you can go check out Jovi's site there. Also, follow on BlueSky, any other socials you want me to share?

JOVI: Those are the most important. Thank you.

JASON: And we are talking about Preact today. Here's the Preact home page. And you can see right here at the top, it is a 3K, now 4K for a little wiggle room, alternative for React with the same API. Nothing about this doesn't feel like React to me, but I'm shipping 20% of the code. It makes me happy. I'm ready to learn about it. What's the first thing I should do here? Well, let's click the get started button, and that immediately shows like all of the ways we can start an application.

JASON: Here's the no build tool, here's the Vite. Oh, webpack, Node, whatever you're using, it's going to fit.

JOVI: Exactly, those are the alias approaches if you want to, if you're coming from React that you want to try to just put us in there. Use the alias approach. And you can trial it. Before we dig into building, we can look at the differences with React, which is in the left hand, if you scroll down a bit.

JASON: Differences in React.

JOVI: That's it. We have listed out all of the main differences. We don't have synthetic events. If you use events with a portal, we won't bubble that true to portal because the portal is a JavaScript concept, not a DOM concept some inputs are different, like in React, they use unchanged to mean on input. Yeah, on changed. Only calls JavaScript when you blur the input. And on input with every key stroke calls it.

JASON: Got it. And looks like a couple others, double click has a standard spelling and React spelling. The other things I thought were cool are things like, you don't have to use class name, you can use class. Upgrades that help me feel better as a developer. I'm closer to the DOM. I know the synthetic event system was really important ten years ago. The browsers have standardized since then. It makes me as a developer feel more future proof with something. Like I always get nervous when we're paralleling universes. A change here to get to me. And then the bigger the ecosystem is, the longer it takes to bubble. Any other major differences you want to touch on?

JOVI: No, those are like the things I find cool, as well, like class, instead of class name, for instead of HTML 4, things like that are quality of life. Just work.

JASON: This one, OK, who can I get a show of hands in the chat if you've been personally victimized by having to edit a freaking SGE to put it in your React code? It's little stuff like this. Eats a couple minutes at a time. It's just nice we don't have to do that anymore. Yeah, got a couple of folks showing up in the chat about this. Let me get back to getting started. You said we're probably going to go Vite today.

JOVI: We have unique things here, Ryan Christian from our team, wrote something called the Vite Prerunner Program, which is a little different rather than it running the render to string, it'll render the pages in puppeteer, take HTML snapshot and use those, instead, which is a really cool way to prerender. We'll look at it in a bit. So we can just run NPM in it, Preact, and we're off to the races.

JASON: All right. So I've got a new project here, I've been using PMPM, is that an issue?

JOVI: I think it's PMPM create Preact.

JASON: Here we go. And we're going to do "learn Preact." We'll do typescript. Do I want the router?

JOVI: Yeah, let's add the router.

JASON: You want to do the prerender, as well?

JOVI: We can show it off, it's pretty cool. Whatever you prefer.

JASON: Yeah, I'll keep it. It's always nice to get the warnings when I'm doing something. All right. We're all set. I'm going to move into this into this project. I will PMPM dev.

JOVI: Yep. And you should be started.

JASON: We are running. So let me get over to here.

JOVI: I hope my fixes to the hot reloading worked. I think it did.

JASON: Excuse me. All right. So, we've got a home page. We've got a 404 page. We've got links to the docs. And I'm I'm ready. Let me open up this up in where am I at? Where's my? Here I go. Open this project. We're going to go into GitHub. And here we go. I'm on a new computer. And so, it's going to be exciting today as I figure out how any of this stuff works. Would I like to open the repository? Sure.

JOVI: Yes.

JASON: I don't want to do that, we're going to skip that. So I've got this project sitting over here. Let me make this a little bit bigger. It seems awfully small. And now, we've got yeah, where if you want to show a tour of sort of what's happening, where's the right place to look to get familiar with the project?

JOVI: In the index of TSX, the HTML file references that. And we're here.

JASON: So the index, yeah the index is just what you would expect in a project like this. We've got our reference to the index, the app we're going to mount into, and here we go.

JOVI: We have this routing library now called Preact iso, Preact iso, our way to isomorphic everything. Basically, conceptually, what it does, when you load a second page that's suspenseful. It'll use the first page as the loader and pop the second page in as soon as it's ready rather than doing the whole loading thing.

JASON: So it kind of it is similar to the way that, say, like Astro's doing server deferred, loads the page with a loading element, and then does kind of a fetch call and replaces the loading element with the contents of whatever was loaded?

JOVI: Similar, but all done in like components, but yeah. Very similar.

JASON: Gotcha. Gotcha. Cool. So, then, you know, if you've ever written React, this is going to look pretty familiar. We've got providers. So that we can do context. We've got you are O header, we've got a router. This is a pretty familiar router, like, API, and then, this, can you talk a little bit about what this is?

JOVI: Yeah. So basically, for our because we enabled prerendering, it will say if we're in the browser, use Hydrate, when there's no elements to hydrate, it just falls back to render anyway. So this is like a safe default for people. They have built and pre rendered the HTML file is available, take the faster route.

JASON: Got it, got it. OK. And down here, we're prerendering so we do this SSR call to load our app with our data.

JOVI: Yeah, that will basically happen when you run PMPM Built, and this part gets triggered.

JASON: Got it. This is kind of exporting the right function so that the Preact Build system has something to hook into. This is us defining our app, and then, if we jump around in here a little bit, we've got pages. We've got components. So, we'll look at this index page. And again, this looks very familiar. You know, we import our styles, just like so. We can import SVGs. This delightful little bit where we're using a class instead of a class name. But otherwise, this just looks like every bit of React I've ever written. We've got our

JOVI: That's our goal.

JASON: Yeah, this is great. This is exactly what I want. We've got multiple components declared in the same file, which is nice to be able to do. You can make that call that you don't necessarily need to split everything out into its own file. I think that's like maybe one of the, one of the drawbacks to something like Astro, for example, you can't do that. And then, when you get into the header, same general idea. We've got our its used location, is this coming out of this is why we needed the provider here?

JOVI: Yeah, location, provider.

JASON: Provider, and this allows us to use the location context?

JOVI: Yep. Exactly. Basically, the location context is very similar to Windows.location, we're reexporting it and well, enriching it a bit.

JASON: Mm hmm, mm hmm. So question, do third party React libraries generally work with Preact? One of the biggest reasons I'm always hesitant to choose something other than React because the ecosystem. The guesstimate is around 90 to 95%?

JOVI: Yeah, generally, they should work. There are some component libraries that have components that don't work. They tend to rely on React internals, like, for instance, that event bubbling outside of the portal or relying on the synthetic event system dispatching the event synchronously, like, if you rely on DOM bubbling, then every piece, every step of the way that it bubbles up, a microtick is less than 3. It's asynchronous. You can't predict in a deep tree when it'll land.

JASON: Got it. So if the if the the short answer is, like, give it a shot because probably it's going to work. And then, if you get into some particularly edge casy components where they're doing something, and honestly, if a component is reaching for React internals, probably getting themselves in trouble anyways because that seems to be one of those things that just sort of generally causes pain for companies. But, so, all of that being said, it's likely to work. And I know I've I've dropped in the Preact Compat a few times on projects, it tends to just work. I haven't hit, I don't know if I've ever actually hit an issue with it. It sort of does the thing and it feels like, feels like magic.

JOVI: If it doesn't work, you can yell at me on GitHub and BlueSky, and I tend to reply very quickly.

JASON: Yes, that is another thing that I have really enjoyed about the Preact team, that you all seem very responsive and like genuinely engaged and interested in people succeeding with the project, which is another potential contrast to the way it feels like interacting with the React open source community. Another question here, is how to configure between SSR and I'm assuming that's SSG is the yes, SSG?

JOVI: Yeah, so this is like a prerender template. There are other projects like Vite, I think Vite with a K, Vike, more suited for the SSR scenario. This is like, I want to build a blog or some static sites.

JASON: Got it.

JOVI: This is how my portfolio, for example, is built.

JASON: It was called Preact Vike you said?

JOVI: Yes, sorry.

JASON: Vike.dev.

JOVI: Like ViteSSR framework.

JASON: Got it. There's a resource if you want to do SSR with Preact. And then, otherwise, if I'm using a can you drop in Preact as a layer on a framework that otherwise does SSR? Like can you drop in Preact as the React Core for Nxt or something?

JOVI: Actually, yes. This has worked until V15, officially, and unofficially, it still works, I know there's a community member that maintains this Preact patch thing, and it's absolutely amazing and I appreciate tremendously for it. And we don't officially say it anymore. Let me find the link for that. I need to go to the YouTube channel. There we go. Is it not coming on?

JASON: You can throw it in the private chat and I can get it.

JOVI: OK.

JASON: Copy the link address and coming out here. There's the next Preact. I can put it up on the screen here. Just as a note, if you're in the CodeTV Discord, all of these links get shared to a channel about it's called "show links" that will have a list of everything we shared today. If you're interested in that, you can get into the CodeTV Discord using this link here. We are looking around here in here. We've got our prerender setup. We're using hooks just like we would in React. We've got, you know, pretty comfortable feeling API so far. So what should we do first? In this codebase?

JOVI: Well, just do say we also have fast refresh from React, we made our own version. So that just works. So that's what you're dealing with right now. I tried to fix something yesterday, I hope it still works. I do hope it does.

JASON: Let's find out. I'm going to change this headline here.

JOVI: Yeah.

JASON: There we go. We got hot reloading, fast refresh, whatever you want to call it.

JOVI: Yeah. We call it Prefresh, but yeah, that's

JASON: I like it.

JOVI: The nature of the beast. JA. [ Laughter ]

JASON: But this is great. We've got all of the things I'm used to. I'm working in, working in Vite, I'm working in APIs that I'm comfortable with. I get to use the things I like about the DOM, like regular attribute names, and I don't I don't really feel, so far, we haven't departed from anything I haven't built a hundred times before in React. Which is feeling good so far. So

JOVI: Maybe, then, we built something you've built in React before.

JASON: OK.

JOVI: And then well, when we've done that, we can go to Signals and then, I can show a bit, like, how that feels.

JASON: Yeah, let's do it. Is there a specific component that you like to use for this example?

JOVI: I'm a to do man, but that's maybe not exciting for people.

JASON: I don't really mind because I think something that we can put together quickly, I think, feels good. Gives us more time to explore some of the other pieces. So to do and to do this, we need

JOVI: Like a to do list component and create a little to do component to

JASON: OK. To do list, I'm going to get into naming battles here, but I don't mind. We're going to do export function to do list. And then, inside of here, we need a look at it help. OK. So we've got this. And now oh, look at that. We just gate right out of

JOVI: It just knew.

JASON: It just knew. And for our to do, yeah, why not? OK. Apparently, my cursor tokens I don't know. Do we want all of this?

JOVI: Yeah, they are there.

JASON: Is this new to do, looks fine. We'll do that. And then, down in here, we should be able to do something like something like semantics, and something, new to do, and we've got an on change. But in this case, we want it to be on input, right?

JOVI: Exactly. And we probably want to use current targets over target for safety. Target can be the proper thing.

JASON: Current, there we go, current target. And we can keep that add to do. OK.

JOVI: Ryan Christian added something really cool where if you add a property to a DOM element that doesn't exist, for example, if you do auto play of the input, it'll yell at you. So

JASON: Oh, interesting.

JOVI: All the allowed attributes listed for each element.

JASON: If I yeah. Nice. That's great. That is very great. So this is pretty basic, but I think it'll let us get where we want to go. And I'm going to clean these out to give us more space to operate with. We'll get our to do list. And here, we've got our to dos, and we'll say, test the app. Oh, I broke it.

JOVI: Disappeared.

JASON: Yeah, what did I do?

JOVI: I think what's happening, that the form submit is reloading the page. You'll need to provide the

JASON: Oh, OK. In that case, we need an on submit, and we will just do an event, event prevent default and then, theoretically, here we go.

JOVI: Exactly.

JASON: That gets us closer. And down here, do you want me to break this out into another component? Or do you have preferences for how we do this later?

JOVI: Let's do that because in the Signals thing that will make our lives easier to explain certain concepts. Well, just the list item.

JASON: Just the list item? So I'm going to build it down here.

JOVI: Yeah.

JASON: To do item, we'll do one of these, and then, OK. So we've got same general idea here, we'll be able to add that's what I wanted. We've got our check box. Let's leave this out for a second, we'll come back to this. But what we should be able to get right now is just our list with our check boxes. Good. OK. So then, in here, when we go to do the is it still on input in a check box?

JOVI: I think you can do both because clicking it auto blurs it. But yeah, you can try.

JASON: Yeah, this is fine.

JOVI: You might have noticed that the that it already worked when you were toggling it. That's because we don't have real controls components. Like, if they don't cause a render, then we'll just respect a DOM value.

JASON: OK, all right. Yeah, you know, I like that. That makes me that's controlled components and React are one of those things that in very, very few cases, I want that. Most of the time, I just want the DOM to be the DOM. And so, I'm happy to see that just kind of did what I expected instead of me having to go, oh, right, we're in React, needs to be controlled, et cetera, et cetera. So then, when we've got this, we've got our handle to do. And so, this, actually, starts to, this starts to give us a challenge because we need to share the state between these two things. So we're going to need, what? We'd have to set up a context and a provider and be able to use to dos somewhere and those sorts of things, right?

JOVI: I like to keep things simple, give it a callback.

JASON: Oh, you would do a callback up here?

JOVI: Yeah, and give it to the to do item.

JASON: We'll set to dos, good, to completed. On toggle? Is that a thing?

JOVI: Anything is on your components.

JASON: I guess that's true. We're going to go with on inpresident uh, I think. And we will update our on input to have the ID and, yep, that's good. And so, then, we're going to change this to be on input. To do ID and whether or not it's checked. And that should, I think, I think that works.

JOVI: Let's see. We might need to do a log

JASON: Yeah.

JOVI: That's the confusing thing with the the not completely controlled thing.

JASON: We'll log each of these to dos as they come out. And we get first one's marked just as complete, this one's marked as false. Et cetera. We click, again. And now

JOVI: Perfectly.

JASON: It's doing exactly what we expected. All right. Vibe coding getting us.

JOVI: I call this winning. [ Laughter ]

JASON: I'm pretty happy with it.

JOVI: Same. All right. So, this is all cool but you might have noticed that, like, when we change anything, like everything we rendered, and the cool thing with signals, which we need to install. So it's at Preact/signals.

JASON: OK. Let me... uh...

JOVI: Which one is that?

JASON: This is Warp. And it was @Preact/signals?

JOVI: Exactly.

JASON: I need to actually install it. Yeah, Warp is great, each thing is a block. If you want to copy the whole block, it like lets you do that instead of having to go back and select and everything. Lots of things to like about it. Maybe should talk about that on another day.

JOVI: Yeah, I mean

JASON: Test the app.

JOVI: I'll go on a small tangent here to like show why we really like Signals. To do that, let's just go to the global scope of your app just to show one little thing and we'll build from it. So, yeah, just go outside of all of the components just in the global scope, recreate a signal, for instance, count.

JASON: You want this in the to do list? Or

JOVI: Just here is good. Just so we can show the pull list values. It's equals signal and round braces, zero.

JASON: OK. And came out of signals.

JOVI: Yeah, and then, we'll make a "compute it" underneath called "double," for example.

JASON: Double is computed.

JOVI: Yeah. And inside of that, after we import it. Inside of that closure, can you add a log that says, icon count or whatever.

JASON: OK. .

JOVI: And then, underneath, add a log, well, underneath the compute it initialization, well, yeah, that's also good. Add a yeah. And then, add count, well count.value, for example. So the thing I want to show you is despite us making the computed here, it won't calculate its value until the signal becomes active.

JASON: Ohhh.

JOVI: Exactly.

JASON: OK.

JOVI: All of these things are lazy.

JASON: And if I take these out, then, it shouldn't be called at all?

JOVI: Well, we still

JASON: It's not. Look at that!

JOVI: Because we're not accessing double anymore.

JASON: That's pretty slick. It's not attempting to run this. Despite being called right here. Because we don't ever access the value. That is really slick. And then, I guess the is it this easy?

JOVI: Yes.

JASON: Oh, sick. Wow.

JOVI: And the cool thing, now, is that we can add a lock to home, and you'll never see that render getting called when the signals are updating.

JASON: It calls it once, and not, again. See this is that kind of stuff that it's just like I see why people get so excited here.

JOVI: Yes, it's fun. It works, it's performance, and it's not that different. It's just you have to grasp the concept and we get there.

JASON: It doesn't feel substantially different from how you state works. It's even maybe slightly more intuitive, I guess, because you don't have to call like a set state, you just interact with the value. And you would expect if I call a function that interacts with the value, that would change, what did I just do? That would change the value. And then, you know, knowing that it's a signal, knowing that whenever this value changes, this computed call gets called, again. Definitely some things you've got to learn. That is really nice.

JOVI: Yep, and just to highlight. We still use VDOM. If you change the home rendering thing to use count.value. Second argument, count.value, or whatever. Then, it will get called and it will rerender. It's smart enough to know which tracking context it's in. It's like, I'm in a text note, I can do an optimized thing. I'm in attribute, and then, so on.

JASON: That's very slick. OK. So let's apply that to our to do list here. And let's make this work. So, up here we were using use state.

JOVI: That can be use signal now. In a component, we have to still use the hook equivalent because we want to memorize the signal so we don't recreate it on every render.

JASON: Got it. And same here, use signal.

JOVI: And the return isn't like structured, it's just a value.

JASON: Like so?

JOVI: Yeah, exactly.

JASON: OK. We got that. And then, down here, when we want to add a new to do, we just what? It's just to dos

JOVI: Yep. To dos.value =

JASON: And then this without the parentheses. And new to do value is going to be empty.

JOVI: Yep. Online 16 and 17 will also need use.value.

JASON: Right. Right.

JOVI: If you don't want to subscribe to something, we have an untracked callback and we have .peak. I try to say in 95% of the cases, you don't need peak. You kind of want to subscribe and in event handlers, you're not in a tracking context. So whether you're reading the value or not, you're not subscribing to anything.

JASON: OK. And this is our to do.

JOVI: Yeah.

JASON: And does this let's see, that's mapped properly. Good, good, good. OK. And then, I just have one extra

JOVI: Good.

JASON: So that gets us there. And now, we've got some errors down here because we're setting new to do.

JOVI: We call it handle add to do, cursor thing.

JASON: And down here on input, this is updating the new to do value, so we need to do a new to do

JOVI: Oh, right, yeah.

JASON: New to do value equals

JOVI: Yep, exactly.

JASON: I can get rid of this. And our last error down here is that we are not mapping over the value. So we'll fix that. Now, no more errors, theoretically speaking, we're ready to rock. Let's test this. And so when we submit, it rerenders, but watch. Before, it was rerendering every time I typed. When I click, it does it. JA.

JOVI: We'll further optimize this, we're calling the values, but we have the helper in Preact signals which is called for. Like the loop thing. F O R.

JASON: And it's a

JOVI: A JSX component, sorry.

JASON: Gotcha. I'm going to do it here?

JOVI: Yeah. And cursor is very correct. It's each to do.value. And then, we have like closure that we can call, so basically, it has a callback.

JASON: And that's going to have my to do?

JOVI: Exactly.

JASON: I've got my to do, and I can loop through this.

JOVI: Not sure, you don't have to call value in each. It can just be the signal. And then, it all just works. And now, you'll see that the outer closure, the to do list itself doesn't have to rerender anymore. Because no like nothing in there is subscribed to the todos signal.

JASON: Hmm.

JOVI: Or maybe there is

JASON: It's just the one.

JOVI: Yeah, exactly.

JASON: Now instead of rerendering the list, just the one thing we changed. Very slick.

JOVI: Yes. So that's, we can create these really fine grain trees with these little components, and do whatever we like. We can do similar, we can transfer signals over context, but it's a box. So unless someone accesses the value, you won't rerender.

JASON: Oh, nice, we can do things like, I can pass todos, no matter what components get todos until I hit todos.value, we don't trigger a rerender?

JOVI: Exactly.

JASON: Slick.

JOVI: You can create, you can have the ergonomics of React complex, but you're passing around boxed values.

JASON: I like it. I like that a lot. And so, this, then, feels again, this feels, I've seen this approach, this kind of for component before. I like it, I like this pattern in React, I use it all the time of kind of passing a callback. And again, nothing here feels particularly wild. I'm honestly, it's kind of a relief to be able to get all of the set calls and you can just say, all right, I'm going to update the value of this thing. And then, I know if I'm hitting .value, I'm going to trigger everything and the same here. It just feels more JavaScript y to me, and less sort of like a super set of JavaScript.

JOVI: I agree with that. It can sometimes in large applications be very hard to debug. I've been working on like a debug signals package that helps with that, but yeah. Very early still.

JASON: Got it. Got it, got it. Yeah. All right. Well, so this feels, I feel like we've got ourselves what I would consider to be a nice little to do list. It feels like it's doing what I want. Do we what else do you want to show off here?

JOVI: I mean, the last piece for React developers would be like how do we have side effects? And we have this little thing called use signal effect, which is basically a side effect that will eagerly run compared to the compute it, which is lazy.

JASON: OK.

JOVI: And register the dependencies and every time a dependency changes, it'll rerun, which is different than user effect, which you have to explicitly say "rerun when these things change."

JASON: Got it. Do you have a good ready example for what we should do to show that off?

JOVI: We could just look at the todos array.

JASON: OK.

JOVI: Anything, really.

JASON: Yeah, we can do that. Let's see let's do this. Let's create a component down here. This is going to be a all done component. And it's going to if every todo is completed, show a big smiley face. That's, I think, a pretty reasonable thing to do. So, for us to do that, we need to receive the todos, which are going to be our array of todos, and then, in here.

JOVI: Our signal.

JASON: Array of signal is that annotated differently?

JOVI: Yeah, capital Signal, and then the generic, lesser than, bigger than.

JASON: What am I doing wrong? Oh, there it is. I hadn't finished typing it. I need to grab this out of Preact signals and now oh, it kind of already did the thing here.

JOVI: We can do it with a use signal, where it's done.

JASON: Yeah, I like that. Is done equals use signal, and that is going to be a boolean that starts out as false.

JOVI: Yeah.

JASON: OK.

JOVI: We do our use signal effect.

JASON: UseSignal effect, and that is going to does this work the same way as like use effect in React where we've got our dependency of arrays at the end?

JOVI: Well, we don't need them because every time you the value, we can automatically consider a dependency.

JASON: OK. And so, this here, is this correct?

JOVI: That is correct.

JASON: OK. We've got this.

JOVI: To show off the last component, we have to show, we can return a JSX component called "show" here.

JASON: Great. Let me make that way easier. So we're going to I'm just going to show when isDone value, and that is going to be our span here.

JOVI: We don't need the dot value, we can pass in the signal, but that should be all you need.

JASON: OK. So, when we're done, we want to show this thing, and then, up here, we're going to use this component and pass in our todos.

JOVI: Exactly.

JASON: So out here, Everything is done, and we'll say test Signals, OK. And there we go.

JOVI: That effect now is automatically subscribed to whatever you accessed and you don't have to worry about what dependencies or whatever.

JASON: This is, like, this is that really nice the ergonomics that I want, right? I like that it can just tell this is what's been accessed and this is what's been accessed. As opposed to saying an ES lint rule that knows what's been accessed but yells at me to fill out an array to tell what's been accessed. But you already know. You wouldn't be yelling at me if you didn't already know. This is cool. I really like that, again, it just kind of feels like, all right, if I want to do something whenever something changes, easy. I get it. That's really nice to work with. I like that a lot. So from here

JOVI: Sorry.

JASON: From here, this was kind of talking through Signals and what this would be equivalently for React developers. Was there other things you wanted to touch on that maybe go beyond?

JOVI: I think maybe one thing I think about currently on a daily basis is, disconnecting this whole the whole state level from the view. State tree was very early in that. And I try to draw inspiration from that. Where we can define models and actions and views for that model. That automatically get computed and actions you can perform actions on the model from the view but it's disconnected all of a sudden.

JASON: OK. How and so, is this something that is like do I come in here and create a models folder? How are you structuring this?

JOVI: Yeah, I am currently, that's how I think about it. Like

JASON: OK.

JOVI: We create a function like "create todo."

JASON: OK. And this is this a tsx or

JOVI: No, it can be a ts.

JASON: In here, we have a function called "create todo."

JOVI: Yep. And it returns like all signals, for example, like every property can be a Signal so you can do the really fine grain stuff.

JASON: OK. Whoa. Calm down. Calm down. So we're going jeez. OK. I would like to be able to press tab without you. There we go. I'm really trying, I'm trying to give it the old college try here. On using these tools and there are a lot of things I love about them and a lot of things that drive me up a wall. And hijacking all of my buttons is one of them. So in here, we want our to do.

JOVI: Yep.

JASON: You want each. Sorry, which

JOVI: We have two streams of thought here. Either every todo is a signal, or you make every leaf of the todo a Signal so if you update like one property, you do fine grain reactivity.

JASON: So, meaning... we would have the text of the todo and the completed status be signals individually?

JOVI: Exactly.

JASON: OK. So if we have our text and that would be a signal

JOVI: Lowercased.

JASON: Right, right. Signal.

JOVI: Other one is a type.

JASON: Yes, you're right. You're right. And in this, it would be text. Do I want to generate the ID here?

JOVI: Well, you can take it as an input or return something random. The cool thing here, now, is that

JASON: Yeah, I have my this is fine. And then we'll just OK. Nobody look at that. Everybody

JOVI: The cool things you can do with these models. Oh

JASON: Oh, that's it. We'll now we're on our own.

JOVI: Now, you need to get sponsored.

JASON: Yeah, cursor. [ Laughter ] OK. So you were saying the cool things with these models.

JOVI: Yeah. The cool thing with models, you can add a function to the return array called toggle, for example.

JASON: OK.

JOVI: Sorry, behind completed, you can add, yeah, const toggle equals function, which toggles the completed status and returns it as part of the object.

JASON: Oh, I got it. This would be completed value.

JOVI: Yeah, completed value equals.

JASON: Oh, you can't do this, you've got to do it this way.

JOVI: Yeah. That would be awesome vx, though.

JASON: Completed.value equals the opposite of completed.value. And then I can return toggle.

JOVI: Now, your state is next to the things you can do with your state, which is pretty fun.

JASON: Got it. Got it. And do you want to wrap this up into to do list? Or what's your mental model on something like this?

JOVI: We can do this because we want the list to be reactive, as well, but it's just a list of like a signal of an array, we can just not do it. We can do create todo in todo.tsx.

JASON: So we come out here and what we're going to do with our let's see, here's our todo. When we handle our add todo, this is going to

JOVI: Yeah, there it is.

JASON: This is going to use create to do and it doesn't have an ID. It does have texts. OK. What are you mad about? Property ID is missing

JOVI: It just wants it just wants new todo.value, not an object.

JASON: It does? I think it wants no, I think it's property ID is missing.

JOVI: Oh, right not returning an ID.

JASON: Yeah, I think it's mad because it wants an ID. Why don't we create one? It's going to be easier for us.

JOVI: Yeah.

JASON: Yeah. Isn't there a cryptoUU ID? I'm not going to stress about it. We're going to map.random it because I can't remember what that crypto thing is. And I ran out of auto completes. So in here, our

JOVI: Now complaining about what I said.

JASON: It still wants an ID. So I guess we're going to have to map.random out here.

JOVI: So create todo only wants a string. That's the only input we gave it.

JASON: Oh, you're right. I'm sorry.

JOVI: No worries.

JASON: Now, what? Type ID, text, completed. Not assignable to type todo because we added a toggle.

JOVI: Yeah. And these are signals, now, the two things above.

JASON: This is a signal. And this is a signal. The toggle is not, the toggle is returning boolean, oh, wait, is it returning at all? Toggle is returning, yes, a boolean.

JOVI: Yeah, kind of.

JASON: Sort of. Should we just have it not return anything? That seems like a safer move and then we can explicitly void it here. And now, we're not going to try to rely on that later. OK. So we get our new value. We're dropping in our to dos, we have our create to do value, and then, our new to do value drops back to zero. And then, to dos value down here is broken because we need to do some stuff with it.

JOVI: Yeah, now, we basically rather than having to remap that whole array, we can just, instead, look for the item and call.toggle.

JASON: OK. So when we do yeah, do we even need the handle toggle? It would almost be like down in here?

JOVI: Yeah, now we can just

JASON: Drop that entirely. We drop this entirely and drop this entirely. And then, down here, on input, no

JOVI: Yes, there you can just do todo.toggle.

JASON: Todo.toggle. And that's a function.

JOVI: Yeah.

JASON: We don't need the event anymore. And we can

JOVI: Don't need to type anymore.

JASON: On input. OK. Let's format this up. OK.

JOVI: Haven't saved the todo.yes file.

JASON: OK. That's saved. Whoops. This, as well. Just all right. Theoretically speaking, then, this should all work. So we're going to test model. Look at it go. But we do have one problem. Which is that we have now broken our derive state in all done. Because it's not doing the same thing anymore.

JOVI: Because now it's looking at todo.completed, which is a signal. It has to look at todo.completed.value.

JASON: Look at us go.

JOVI: The model thing kind of disconnects all of this logic and allows you to just in your components call these actions.

JASON: Mm hmm.

JOVI: I find it very ergonomic, personally, and makes everything reason in isolation.

JASON: What we could also do with this, kind of looking at the model here. If we did make our list into part of the model, then this logic could live in it and we could have like the list.items and list.all complete. Or list.completed or something. Where these sorts of things now are bundled up. And in my code itself, I don't need this anymore.

JOVI: Yeah, it would basically be a computed on the store that contains the list.

JASON: I could see how that would clean things up and it would be nice from a honestly, what else did you want to show? We've got about 15 minutes of coding time left.

JOVI: That's basically what we have.

JASON: Let's make this list into let's clean this up. I think it'll be really nice. If we have our function of create to do list, then this one is not going to have any input because it's just going to return an empty list. But it's going to give us the list, which will be a signal that is one of these and it's going to have the to do, right? What button? There we go. And I need to get my to do out of here. And we will export this from here to still use it. Now, it's starting to turn into a little library inside of our inside of what we're doing here. This is going to be a type of signal. So then, in here, we had won't need that anymore, we can get our create todo list and also, the type of todo. So that things stop yelling at us. And then, out here, we've got our create todo list. And then, to bring in this this function. We can bring this logic in and refactor it.

JOVI: What we can do is return an object that has the list. And the computed all done.

JASON: We'll do a signal of

JOVI: We don't need a signal, we can

JASON: Doesn't need to be.

JOVI: We can compute it.

JASON: You are absolutely right. So then, the completed is going to be a computed that will return the

JOVI: Yeah, exactly.

JASON: It's going to be list this time. So now, that's our completed. And down here, we return our list. And completed. OK. So that's our model. And this is already, this is nice and clean and I like this, this makes me feel good. And then, in here, we get rid of this one. And instead, we've got our to do list and our to dos are going to be

JOVI: Basically, instead of the array, you have to, if we call to do lists on every render, it'll just keep refreshing.

JASON: That's a good point. Good point. OK.

JOVI: We have to call that as part of the use signals, either initial value or create globally. We can just do global state. It will work.

JASON: We can do something like to dos equals create to do list. And then, we would drop this in here.

JOVI: You can just remove that now.

JASON: Oh, right. We have it.

JOVI: Yep.

JASON: And so, in here, we need to change all of these to be list. Because that's what we're using now. We're accessing our to do list. And this is our to do list. And this is actually going to change, so we don't need to do that the same way. And for each todos.list, and finally, down here, we are actually going to skip this part entirely. And instead, we can go show when todos completed.

JOVI: Exactly. That's all you needed.

JASON: Just to double check our code and clean things up.

JOVI: Well, we need to clean one more thing up, and that's how we add the todo. That can be part of our todo list, as well. An action alongside.

JASON: Good point. Good point. Let's do that. We'll take this and bring this into our create todo list and that's going to be const add. And how do we do this?

JOVI: Yep, there you go.

JASON: That's messy. Handle add todo and create todo is living up here somewhere. Create todo is in our model. So we can we just need to get the

JOVI: Just have to use the list, which is already in the closure here. Yeah, and get the text, you're right.

JASON: OK. We can do list, list. And create todo, todo text.

JOVI: Exactly. You will have to assign list.value and call list.value on there. Yeah. That is entirely correct. We just need to do the assignment.

JASON: Right. You're right. You are absolutely right. List.value = list.value. And we'll do one of these. And then, we to clear that new todo, I don't need to do that now, do I?

JOVI: We still need to do that to reset the input. We would still probably do that in the callback there. That's the component. Now you can call add.

JASON: Todo add would be newtodo value, and we've got our newtodo value here. And so we are really tightening up the way this works. We don't need to create todo anymore. Which is pretty slick. And we are in, like, this is pretty tidy. So just kind of walking through this. We create our new todo list, in the global scope, and it's a signal. Then, in hour component, which is what renders, we keep track of the input value, what people are actually typing. We have a function to handle a new todo, which uses the add helper we created on the todo list itself. We show a component whether things are all done, and the way that works, it just looks at the todos.completed. And again, we don't have to pass that in because it's in the global scope. And then, we use the, we have a forum input that on click handles our run todo, using the add helper and setting the local value. And down here, we use the for helper to go through our todo list, and that's where the array to render these todo items, which are taking one todo instance, setting whether or not it's completed. And it has a helper function on the todo item itself to toggle whether or not it's complete. This is clean, really clean. Make sure it works. [ Laughter ]

JOVI: The eternal issue.

JASON: And now we've got, so each of our items, we're able to check them. And when they're all checked, we get our status that we would expect. Like

JOVI: Yep.

JASON: That's clean. That's really clean.

JOVI: That's been top of mind for me. Having that models approach being something really scalable and easy to give to people, that would be very fun.

JASON: Yeah, this is this is nice looking stuff. Let's see, we've got some commentary. Ah, yes, crypto.random U, thank you for looking that up, Nikki. This is more or less how I'm handling the Svelt app. Yeah, this model approach is really nice thinking around that. I don't know, how so, jeez, how would this be different from something like vaulteo. Are you familiar with that? I'm not.

JOVI: So, I am not familiar with Vaulteo itself, but most of the React libraries and Signals React does this, as well, when you access a Signal, it needs to rerender the whole components. So, you lose some of the fine grain reactivity or if you don't want to lose it, you have to really work around it to make it work well for you.

JASON: Got it, and we're talking about Valtio if you want to dig into exactly what that is. Not familiar with that one myself. Finely tuned reactivity is very important, yeah, absolutely. Looks like Vaultio, using layout effect behind the scene. Got it. Owen is asking, do you know anything about the Preact fork of Remix?

JOVI: Uh I also, don't know.

JASON: I also don't know. And every time I see somebody try to guess what the Remix team is up to, we're wrong. [ Laughter ]

JOVI: Well, they

JASON: Can't predict their movements. OK. Cool. So, anything else we want to cover? Chat, now's your chance if you've got questions, throw them in right now. Jovi, is there anything else you want to highlight? Talk through quickly? We've got another 5 or so minutes on the clock.

JOVI: Um nothing currently springs to mind.

JASON: OK. So let's take this as an opportunity, then, to do a quick recap. So, Preact is a almost equivalent API for writing the kind of the React DX we know and love. It has a compatibility layer, which will cover the vast, vast majority, 90% to 95% of the React ecosystem. It also allows you to do things like write regular attribute names, class, for, et cetera. You can write your SVGs without editing all of the property names, like your stroke with and stuff like that. And, it introduces over the top of React hooks we all know already, like use effect and use state. It introduces Signals, which allow us to use either the React hook style use Signal, or you can use Signals in a way let me pull this back up you can use Signals in a way that let you define these models and build out these really tidy state management packages that are, you know, this is not JSX at all. This isn't React or Preact, this is just state management. And inside of your components, it makes for really, really clean code. Where you are able to whoa. As long as I'm not putting things in weird ways. It makes it easy for you to interact with your data without having to manage it inside the components, which I think a lot of us are guilty of, I know I definitely am.

JOVI: Also plays in nicely with the fine grained reactivity where you alter something you use it.

JASON: Yeah, and I guess to recap on that. The way we've done these Signals, we are only seeing, if we go to the console here, as we let me refresh. We rendered the home page and we've got these bits here. So there's a count where computing a value, which is doubled and that's been called once. And then, as we increment these values, we don't see home rerender as we do that. And down here from our components, we are as we're manipulating on input event in React by default and before we had the signals in place, we were rerendering this entire component tree every time we had an any input. Now as I say test, again, we see it's not rerendering every time. And when I go to add, each one of these is coming in individually and when I go to change them, it doesn't appear to have to rerender. Wait.

JOVI: Yeah, because the attributes can be fine grain reactive.

JASON: The attributes themselves. We made this even more optimized because now, changing the value of the todo doesn't require us to rerender it at all.

JOVI: Yep.

JASON: That is extremely cool. And then, as we go through and do something like hit this derive state up here, this is able to pop in, again, without rerendering our components. So this is really powerful stuff. Especially when you start talking about performance and, you know, getting millions of items up on screen and having these sort of like infinite lists and huge, huge DOM trees you'll see with social media or dashboards or reporting tools where, you know, there's tens or hundreds of thousands of items that need to be displayed in big lists. This is the kind of stuff that makes an enormous difference.

JOVI: Yeah, full disclosure, View, Solid, Svelt, don't try to call the function closure ever again, we still have that where you go back to virtual DOM. But yeah. It optimized that, as well.

JASON: And this is where we as developers have to make the tradeoffs, the nice thing is that you get this sort of pure mental model. It is a function. When something changes, the entire function is called, again, which renders the page, again. And in a lot of cases, this is kind of what you want. If you've got a component that's completely derived from data you're dropping in via props, when the props change, you want the entire component to rerender. You don't necessarily want to lose that every time. So it's nice to be able to have the choice, right? But yeah, I think this is something that's a good callout. This isn't exclusive to Preact, everybody does it except for React, which is, again, you have to look at the ergonomics of things at what you know, what you're fast with. And then, looking at how you can solve these problems in your own projects moving forward. You've got a couple more questions here. Does Preact have something for View transitions?

JOVI: We don't have anything natively. We've pasted some code and that code is used in various big apps. But we don't have anything that we export to like here are View transitions.

JASON: Got it. And would this work for any dropping in of React Native?

JOVI: I know that there are people who have tried to make Preact native, which was basically just the bridgeport. I don't know if I could find the repo fast. I don't think there's a good working equivalent for that yet.

JASON: Yeah, let's see. Experimental, yeah. So if you want to venture into unchartered waters, sounds like there are places you can start.

JOVI: We as the Preact team haven't done anything in that area.

JASON: Got it. OK. Well, I think that is a good place to call this one done. So, we were able to build quite a bit here. I will get this code committed and pushed up to the Learn with Jason GitHub, you can find that. We'll drop that link into the description for the show. And we've been talking to Jovi, let me drop a handful of links for you here. Anything else that people should do if they want to take this further and learn more? Any other resources that you recommend or places they should go to dig deeper?

JOVI: Um, get started with Preact if you run into anything, open a GitHub issue, join our Slack, which is at the top right of the Preact site. We are extremely responsive. We love to talk to people. We love to get feedback. That can be critical feedback, as long as we can improve that it's valuable to us.

JASON: Excellent. Jovi, this was amazing. Thank you so much. Let me do another quick shout out to our captioner. We've had Diane with us here all day making this show more accessible for everybody by adding captions, those are always available at LWJ.dev/captions. We've also got a lot of good stuff coming up on the show. We are going to be doing let's see, where's the we've got oh, come on. Don't do this to me now. We've got a lot of good stuff coming up. We're talking about Preact today. This one has been rescheduled, we're not sure we're going to be able to make it happen. But we do have a replacement coming up. We're going to learn about something called hashbrown. It's pretty interesting stuff. And then, we're going to be learning at SSH for SFTP, you can go to the CodeTV newsletter. If you're enjoying this content and want to see more of it, please, do hit the like button, subscribe, share it with your friend, leave a comment, and maybe even bug your boss to sponsor CodeTV, it's about 5 bucks a month. It does make my day every time somebody signs up. Thank you all so much. Thank you, Jovi for spending time with us today. Any parting words before we call this one all the way done?

JOVI: Thank you for having me. Thanks to everyone who asked questions, and this was very fun. Thank you.

JASON: All right. Thanks, y'all, so much. We will see you next time.

JOVI: See ya.