Going Deep on Svelte 5
with Rich Harris
Svelte’s Runes made a lot of headlines when they were announced. In this episode, creator Rich Harris will teach us about Runes and a whole lot more of what’s new and exciting in Svelte 5.
Topics
Resources & Links
- https://www.streamtext.net/player?event=LearnWithJason
- https://github.com/Rich-Harris
- https://svelte.dev/
- https://svelte-5-preview.vercel.app/
- https://svelte-5-preview.vercel.app/docs/introduction
- https://wiki.c2.com/?CommandQuerySeparation
- https://www.learnwithjason.dev/schedule/
- https://svelte.dev/chat
- https://kit.svelte.dev/
- https://sveltesociety.dev/
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. What is echoing on me? Hush. You okay? Today on the show, we're bringing back one of my all time favorite people to talk tech with, Rich Harris. Rich, how you doing?
Rich: I'm doing great. How are you doing?
Jason: Am also doing great, and my stuff is out of order, so I'm just going to drag them like this. And look how beautifully that works. I'm in the new system, y'all. We're live on all platforms here. I think we're on LinkedIn, Twitter, Twitch, YouTube. Just throwing a regular old Svelte party. So, Rich, I imagine that most of the folks who are here have heard of you before, but just in case, for folks who aren't familiar with you and your work, do you want to give us the high level?
Rich: Sure. I am probably best known as the creator of Svelte, which is the UI framework that we're going to talk about today. I've had a career in open source going back to, I think 2013 was when I first started putting things on GitHub, overcoming the stage fright of putting code out in the open, and I've just been tinkering away ever since then. The other thing I guess I'm known for is rollup the module bundler, which I created, like, 2015 and then left to smarter developers than me to maintain shortly after. And I work at Vercel. I'm an engineers at Vercel. I work exclusively on Svelte, on open source, along with two other people working full time with me, Dominic and Simon, plus the rest of the open source maintenance team. And I've been doing that for two years. I just passed my two year anniversary at Vercel.
Jason: Very good. All right. Two years. That's a lifetime in web dev.
Rich: It is. It's gone in the blink of an eye.
JASON: Yeah, no kidding. Okay, so I've been, I would say, casual user of Svelte. I've never become somebody who uses Svelte as my daily driver. I think more because I'm lazy and I already knew other frameworks rather than because of any actual preference. Every time I've used it I've been super impressed with it and it sounds like we're about to get absolutely massive improvement to what is already a very loved framework. 5 is coming, it's not quite out yet, it's in beta. Can you give us the 10,000-foot version of what it is and why you're excited about it?
RICH: Whew. I can try. It came out in 2019, that was a pretty radical departure from 1 and 2, which is ancient history. 3 is where we basically set the tone of what it's like on Svelte. When if you have a value, you do that by saying count plus equals one or count plus plus. And the compiler recognizes that assignment. You don't have the indirection of an API, you don't have any tricks involved. You're just dealing with the language itself. And so that worked out pretty well. We had a lot of nice features in Svelte 3, you can have styles, CSS, directly inside your components.
We've got primitives for element transitions and motion and all of that other stuff. It was like a pretty good and comprehensive framework. And then in 2021, we added a meta framework on top of that, which is how we recommend people build their Svelte apps. That went pretty good too. A few months ago we released Svelte 4 which was basically Svelte 3 but with some technical things removed, stuff like that just shrunk down the package. It was essentially identical to Svelte 3. And so we had this framework. Basically nothing has changed in Svelte since 2019, which, you know, you say that two years as a long time in development, but 2019, in the framework world, is ancient history.
JASON: For real.
RICH: Even though people tend to regard Svelte as like the new kid on the block, a bit of a hitch to the framework, it's actually one of the most boring frameworks that there is, it's been unchanged in four, four and a half years. Unheard of.
In that time, we've learned a lot about the limits of the Svelte 3 approach. There are only certain things you can do with compile time static analysis. There are certain -- there's the actually done quite work in that paradigm. And we had started to see that as people were building more and more complex apps, they were starting to hit some of these limits. At the same time, there was this larger conversation happening in the framework world around reactivity, mostly instigated by Solid, who evangelized this idea about signals. Not a new idea, they had been around since around 2008, I think, but Solid sort of brought that into the modern area and really evangelized the benefits.
One of those benefits is that you get really fine grain reactivity. Instead of updating at the level of a component, which is what happens in React, or the level of -- a top level variable within a component which is what happens in Svelte, signals allow you to update a single property of a single object, a single member inside a list, and it will only touch for that part.
Signals give us this really nice performance characteristic. And they also solve some of the issues that we had seen with static analysis. So we knew that we wanted to move into this single powered world so we wouldn't hit those occasional performance problems that we had with the Svelte 3 approach.
So that was the background. Then a few months ago, Dominic joined the Svelte team, and one of the things that he very much wanted to bring to the project was this idea of universal reactivity, because I talked about how Svelte 3 introduced this idea of compile time reactivity, bringing reactivity into the language itself, and that's all well and good, but what happens is sometimes you need to have things that aren't inside an individual component, some state that is shared outside the componentry. And the way you've historically done that in Svelte is by kind of deopting, so this completely separate way of describing your reactive state. Svelte has these things called drawers.
And what people have asked for and what Dominic really wanted was to have a way of using Svelte's reactivity anywhere in your app. And so much of the last several months have been spent brainstorming with the entire team, like how can we design such a thing.
And a couple of months ago, we hit upon what we think is the solution. And the solution is something called runes which is I guess what we'll talk about that. The big change with Svelte 5 is moving slightly away from compile time reactivity and into a more run time flavored version of reactivity which gives us all of these benefits.
But there's some other stuff too, new features coming up in Svelte 5, I guess we'll talk about those too. And the culmination of all this, what all of this means in practice is that Svelte 5 is going to be faster than Svelte 4, your apps will be smaller than they are with Svelte 4. You're not going to hit these gnarly edged cases where you bump against the limits of compile time reactivity anymore.
We have some new features. We've removed some of the weirdest parts of Svelte 4. When I say removed, they're still there, you can still use them, everything is backwards compatible, but they're dedicated, we have placements for them. Overall, the learning curve if you're coming to Svelte for the first time just got even smaller. We've done all that, as I say, without introducing breaking changes.
So I'm excited because it just feels like we're jettisoning a lot of the debt that we had accumulated with fixing some of the original design mistakes. And based on everything that we've experienced, as we're building it, it's just a really nice framework to work with. So I'm excited for people to start using it.
JASON: The last time we talked I think is when Svelte 4 came out, you were on the show, and we -- one of the things we talked about was the -- kind of the design philosophy in Svelte, because I've always been impressed by the fact that Svelte isn't a hyperevolving framework, it doesn't feel like everything I knew about Svelte last year is no longer valid because there's so many new ideas that are, you know, completely orthogonal to the way we built things before.
And like you said, the things that worked in 2019 still work today in Svelte. So if you built a Svelte app, it still works, you've got backward compatibility. And this idea of jettisoning a bunch of technical debt and other things as part of going to Svelte 5 is really interesting to me because it's sort of like the image I have in my head, you have a rocket launch, these big, heavy boosters drop off and you're left with the actual spacecraft.
That's a cool way of thinking about it too because it feels to me, you can correct me if I'm wrong, when I look at Svelte and its evolution, that there has been a vision for what Svelte could be. And as the rest of technology has sort of caught up to how that thing can be made possible, you're just able to drop more custom code to get to the vision that you had with, you know, browser APIs, with community submitted things, with these ideas like signals that seem to be kind of coming from all sides at once right now.
RICH: Svelte 3 and Svelte 4, if you have an limit coming in and it has a transition applied to it, what we'll do is generate some CSS key frames bespoke for that element essentially, so as your element is appearing, maybe it's just a simple fate transition and will generate some key percent all the way up to 100%. We'll compile that into a big CSS string. We'll put that in the dom and grab the generated name and put that on the element itself.
And then in that way, we're able to have full programmatic control over the transition, but it's using CSS, and so as long as you're not using any noncompositable properties it will happen fully off the main thread, blah, blah, blah, blah, blah, good for your battery, good for responsiveness, all those lovely things, but it's hella complex, it's hidden for you, it's all behind the scenes. You as the user don't need to worry about that implementation until you do because maybe you have some content security policies which don't allow arbitrary styles to be put in the dom, at that point it becomes a problem, but most of the time you don't need to worry about it.
We need to worry about it. We need to sweat blood and tears to try and get that to work reliably in all situations. Fast forward a few years, we now have this thing called the web animation API and we can do those things directly from JavaScript without jumping through these hoops. Because of that we've been able to get rid of all of the code that was responsible for creating those key frames in favor of just using the web animations API which is so much nicer.
So absolutely, there are lots of cases where, you know, things are kind of falling into place that allow us to get rid of a bunch of stuff. Like, another one would be the fact that packages now use -- can now use JavaScript modules. If you create a native JavaScript it will be native JavaScript modules.
So everything that you import is also treated as a JavaScript module unless you -- like I forget how you answer common JS world but sometimes you can import a common JS modules into an ASM module but not vice-versa, if you start ASM then you can use everything.
So because of that, because SvelteKit is the way to build Svelte apps, we can pretty well assume that if you're using Svelte, you are able to use ASM or at the very least you're using a bundle that understands ESM. Because that have, when we shipped Svelte 4, we no longer needed to actually build the library, we don't need to have a build step before it goes on NPM. We just ship the source code directly there, we don't need to turn it from TypeScript into JavaScript. We just give you the very code that we wrote.
And that is something that just wasn't possible back in 2019 when we shipped Svelte 3. So all of these things, it kind of slowly feels like they are coming into place. And we're just trying to evolve the framework in a way that feels like we're not hanging on to old things, but we're also not leaving people out in the cold if they have components that were written in the old world.
JASON: Yeah, okay, so this is fascinating to me. And you said something like SvelteKit is the way to build Svelte apps and that is kind of following the trend where it seems like the original plan or the original stated intent of the first frameworks that I really used in like any heavy way is, we're building component framework works, it was React in 2014, 2015, whenever it came out, was like, we're building UI, these are little functions that return UI.
And over time, we've just -- we've gone so all in on that as a community, like we only write component-based stuff. Even vanilla JavaScript is pushing toward, can we just do component-based stuff. And I think that's good, we've kind of standardized on a unit of building for the web. And it has also led to what seems to be that every framework has sort of required a meta framework to be able to do, I don't want to say this in a dismissive way, but it feels like serious work, like there needs to be a meta framework of some sort in place, whether that's the blessed one like Svelte and SvelteKit or whether that is a third party one like Astro which can do Svelte components.
It seems less and less common for something that starts today to be a like single page app that's not shipping SvelteKit for Svelte or next for React or Astro for whatever your preferred framework is. Is that me in my Twitter bubble or you seeing usage stats bear that out in Svelte as well?
RICH: If it is your bubble, then it's also my bubble. I think that is basically true. Svelte certainly gets more downloads than SvelteKit. A lot of that is perhaps things that were built before SvelteKit was around. A lot of it is perhaps people using Svelte with things like Astro. Some of it is certainly people hand rolling their own solutions. For a long time, that was just how you did Svelte.
But I think there has been a collective realization that, you know, service side rendering and code splitting and optimizations and TypeScript and you want your CSS to be properly bundled and like all of these things, you want a debt server, you want a CLI to connect all of this stuff together, are you seriously going to build that yourself? No. Nobody's got time for that.
And what these meta frameworks do is they just encapsulate all of the commonality, all of like the basic best practices stuff that is not unique to a specific app or a specific type of app. It's the stuff that pretty much everybody needs. And it just doesn't make sense to reinvent that each time. And beyond that, even, we're starting to see that these meta frameworks are themselves being built on a common base, namely Vite. Even remix is now a Vite powered project and even framework offers -- right, Angular too. As much as Apple doesn't want to keep reinventing the wheel, neither do frameworks. It's a good dynamic and one that we're definitely benefiting from.
JASON: There's this joke, observation, I don't even know what it is, but in the world of tech it seems like we're in a wheel where the two phases of the cycle are bundling and unbundling. And we're currently in a bundling phase [laughs], like we're bundling everything, we're bundling our different apps together, like every company is making like data unification play, we're in a framework bundling phase where we're standardizing on tools like Vite after years of going really wide with how we want to solve these problems, we're standardizing on patterns, we're seeing signals show up in just about every framework now. And I think to me at least that feels like a healthy cycle where we all spread out and we go as far as we can, in as many directions as we can to observe what's possible given the current state of tech and we start to find the bright spots and then we all converge on the bright spots to build the best version of the thing today, but then the world keeps moving and so we realize that the thing that was best yesterday is no longer the best thing today, so we diverge again and we start looking for new bright spots. And it's a healthy cycle, I think I had Ryan Florence talking about Remix and he was describing it as, it's not a circle, we're not going back to what we did before but it's an upward cycle where we keep examining new things with the progression of history supporting us and all the things we learned since the last time we did this, so we're doing it again but better. And that feels accurate. Like in your mind, do you see any -- I'll ask you a very direct technical question, because this is the only valid pushback I've seen against signals, is like as we're converging on these things, some of the things we're starting to adopt feel very much like power tools, where like when you've got a reactive signal, you can put it anywhere, and it starts to kind of -- like this was one of the things that I think made it hard for people to pick up things like RXJS in the past, it felt like the signal could come from anywhere and that made it a little hard to organize and maintain things. And I think we've seen similar pushback against some of the modern framework choices, we're giving people really, really, really powerful tools that require a significant amount of context and expertise to wield well. Do you see that as being -- like are you seeing a similar risk, like do you feel the same way? And if so, how are you -- like how are you addressing that with Svelte 5?
RICH: Wow.
JASON: That was a hell of a statement, I'm sorry [laughs].
RICH: I want to talk about the cyclical thing, because this is a super common misconception that people have. A lot of people think it's just people, you know, maybe who are new to the industry and weren't around for previous cycling just like repeating the mistakes. Swix, who used to be a well-known figure in the front end space but decided to decamp and become a well-known figure in the AI space, he has a quote that I really love, I don't know if it's his quote or he took it from somewhere else, but cynics see pendulums, builders see switchbacks. So oh, you're just going from bundling to unbundling, what is actually happening is we're climbing the mountain together. And at each point, so this is the unbundling phase, and then we get the bundling phase, and that with that shared foundation we can do the next unbundling phase. So we keep sort of moving forward in that way using each period of consolidation as the springboard for the next innovation. That's like a super healthy thing that we should all want to be -- to try to be a part of. I get very frustrated when I see people saying, oh, we were doing this before. We weren't, we were doing a crude version of this in a completely different context. This is new work and it's valid work. So the signals thing, are they power tools, are they overkill. You know, I definitely understand the sentiment there. And I have seen -- I've seen the React team, for example, saying they really like the React program model because you don't have your data wrapped up inside these objects, like maybe it's a function that you called that has another function, like that's the signal way, or maybe it's more like preact signals or views refs where all of your state is contained in proxies, that's a layer on top of the thing you actually care about which is the data. And to the React team, that's unacceptable, they just want to be dealing with the actual values themselves, which is a position that resonates with me and is a position that I respect. I do think that signals are just a fundamentally better way of managing reactivity. So one of the questions we posed ourselves in the beginning of our Svelte 5 design cycle was how can we have all of the benefits of signals but without sacrificing that really nice experience of just working with the raw values. And this is where runes coming in, it's a way of saying to the compiler, this value is just a value. When I do let count equals dollar state zero, the value is zero. And later, when I do count plus equals one, the only reason that that's just not a normal variable assignment is because I originally marked it as a piece of state. But as I'm interacting with that thing, I'm just interacting with a regular old value. So you kind of get the best of both worlds, you get the power tool but you get to use it as if you're writing idiomatic JavaScript. To me it's the best of both worlds.
JASON: We are about 30 minutes in. I want to make sure we've got lots of time to see the actual code go here. I am going to stop yammering at you and start asking you to teach me some stuff. Let me switch over into the coding view here. And I'm going to first do a shoutout to the captioning. We've got live captioning right now. We've got Lee in here doing actual human live transcription. That is available through this website here. You can hit that up. I'm going to figure it out how to pipe that into StreamYard, that captioning is made possible by our sponsors, thank you very much for that. Today we are talking to Rich Harris. I don't know which social media network to link to anymore so I'm linking to GitHub, this is where we are [laughs].
RICH: That's where the pot is at.
JASON: I'm also going to drop a link to Svelte. And that is actually the end what have I know about what to do next. So as somebody who is -- like I've heard about Runes, I'm super pumped to see how this works, what do I do first if I want to give this a try?
RICH: I think my neighbor is doing some DIY, if it's loud. If you're brand-new to Svelte, just go to Svelte.dev and start that way and pretend you never heard the word Runes before because we don't yet have documentation for it. If you're an existing Svelte user or want to see what the new stuff is like, the first thing I would suggest is going to the Svelte-5-preview.vercel.app.
JASON: I'll drop that link here so everybody can see that. We're in. Here we go.
RICH: The first thing you'll notice is the big warning at the top, work in progress, expect bugs. This is not finished software. It's quite finished, it's mostly done, if you click on the status page, you can see all of the tests that are currently passing on Svelte 5.
JASON: So we're not even failing tests, we're skipping tests, that's the only thing that's left?
RICH: Right, we're skipping tests that haven't yet been implemented. We want it to pass CI. Hopefully we want to get to no skipped tests. There are some things that have yet to be finished. But we've tried it out in a few of our apps and it's basically a drop-in replacement to most things. There's also a link to the docs which outline the new stuff if you want to read about it. But the main page, if you click on the Svelte logo to get to the editor, that's where you can actually start playing with the new syntax and the new features.
JASON: Okay. I'm being told our mics are a little different audio-wise. Are you all getting a volume difference?
RICH: If I bring my mic closer is that a little better?
JASON: Let's let StreamYard do the volume adjustment. Somebody let me know in the comments if that is still notably out of whack volume-wise. No issue on your side, big dif, all right. So we're going to roll. Okay, good. Cool. So got the docs, docs links are in the chat. I've got the playground up here. By the way, I love this playground and I love that we've seen -- I don't know if you all were the first ones to do this but I love that just about everybody seems to be picking this model up now because I've really, really liked the idea of learning by doing. I'm actually doing a consulting project with a startup right now and the first thing I suggested was like, no, your onboarding has to be give them broken code and walk them through fixing it, don't just show me the code, because I think it's such a good model of learning.
RICH: It's also a fantastic tool if you're building something. Like Svelte, if you can give people a site where they can try something out and if it doesn't work, instead of asking them to like describe what didn't work or send you a Git repo that you need to clone or paste in reams of code, we can share a link and start diagnosing right away. This saves us more time.
JASON: Excellent. Okay. Before we got on, we pregamed with a couple of ideas. There were several things we had talked about doing. So one was, climbing in here, looking at Runes. Another one was you had mentioned refactoring a Svelte 4 component to a Svelte 5 component to show the differences. I can also just pull up the terminal and spin up a brand-new Svelte 5 beta project. What do you think is the best place to start if we're going to write some code?
RICH: Well, let's have a look at what we've got on the screen right now.
JASON: Let's do it.
RICH: This is super simple, isolated. We can talk about the different features individually. So what we're looking at here is like sort of the hella world of interactive components. If we click on the button, it's going to increment.
JASON: Sorry, I just tried to inhale my coffee. Okay, I'm good. Button is working. I'm crying a little bit but it's not because of the Svelte. [Laughter] Okay.
RICH: For people who are familiar with Svelte 4, this will look mostly the same. The one difference is that online too, we have let count equals dollar state zero. And previously we just did let count equals zero. Anything that was declared at the top level component, if it was mutated and reassigned somewhere and it was referenced in a reactive complex like the template, then we would turn that under the hood into a piece of reactive state. It was all sort of implicit and magic. For the most part that works really nicely, you don't have to think about it. It doesn't scale to when you want to do that sort of thing inside functions or inside JavaScript modules. So with the state Rune, we are explicitly saying to the compiler this is a piece of reactive state. And that's really helpful for the compiler to understand your intent, and it's also helpful for the person reading the code because it's a lot clearer what they're looking at. And it allows the compiler to give the developer certain hints around how to use it correctly. So if you look at the -- if you click on the JavaScript output tab there, you can see how it's actually working. So that increment handler is the first thing you see, it's been hoisted to the top because it's more efficient to have the handler in one place. You'll see we're doing this dollar set and dollar get stuff. That is a rewrite of count plus equals one.
JASON: Got it.
RICH: And count in that context is something called a source, which is a type of signal. And if you scroll down to where we're declaring it, you'll see that we're first opening our component with that push thing, and then we're saying let count equals dollar dot source and providing the initial value. So inside this compiled JavaScript, if you reference count, you're going to get that source object, but any time you reference count inside the code that the compiler converts, you can just reference it as a value. So if you keep scrolling, then you'll come down to where we have this thing called a text effect, and what this is is a function that will populate the text inside the button with the current value of that sourcing. And the way that signals work is, when these effects run, we say, oh, hey, this is the current effect that's running, and any time we do dollar dot get and passive source, that central entity is like, oh, hey, this effect is one of the things that depends on this value. And so later when we do dollar dot set count, that effect, that text effect is one of the things that needs to be run. That's how signals work.
JASON: Great. Okay. So seems like a fun project for somebody who's trying to learn this stuff, to just dive into the Svelte internals here, and start to, you know -- one of the ways I actually learned a lot of the more complicated JavaScript that I know is, early in my career, back in probably -- early in my JavaScript career I reimplemented the React vdom reconciliation from scratch by referencing their code base because it helped me really internalize the mental model of how this stuff works. And looking at -- like if you're looking at this and it makes you want to go cross-eyed and give up, take a look, because the code's not magic. It's complex, there's a lot going on, but it's not something you can't write yourself. So go in, reverse engineer the source code. Now you all have even -- you've made your source code even more approachable, you're back to plain JS; is that correct?
RICH: Mm-hmm.
JASON: So you don't need special tooling, you can go in and play and figure out what works.
RICH: And that's important to us, even though there's some magic that's happening, this compiler is taking your assignments and turning it into somewhere else, that's magic. We prefer to use the word "magical." It's a magical experience. But what's happening is not mysterious. We're showing it to you. This is what the compiler is doing, just doing the grunt work that computers are good at so that you don't have to, so you get a nice experience without any kind of mysterious like, I don't know what's happening here stuff going on behind the scenes.
JASON: I like that. I mean, this is slick. Like, I also do really like that you're showing the output. It demystifies things a little bit. It gives you a thread to tug out if you're trying to understand how it works under the hood. I never would have thought to like dive in here if I couldn't just look at this output, because I'm not going to look at the source code that gets spit out by the compiler. But this is great, show it to me in the browser, give me a thread to tug at, that's super fun and a great learning opportunity. So, okay. Is there anything else you want to dive into here?
RICH: Yeah, let's talk about the drive Rune. Often in your components you'll have some stuff that is dependent on other pieces of state. Start typing double derived, then do count times two.
JASON: Okay.
RICH: Somewhere inside the component you can say double is --
JASON: Double. All right. Now, an important thing to note here is that I don't write Svelte every day, and this was intuitive enough that once Rich told me what the function was called or like what the -- you know, that this is what it is, everything else just makes sense in my head, and I think that's a good sign for an API, when you're brand-new to it and you can just kind of go, I know what I'm doing here, I know what happens next. So that is great, that makes me really happy to see.
RICH: The third Rune that comes after state and derived is the effect Rune. This is sort of the trinity of signals. You have your sources, you have your derived state, and then you have the things that happen when either of those things change.
JASON: Mm-hmm.
RICH: Everything below the script, everything in that template area, this is inside what we call a render effect. But you can also add your own effects. If you want to log out the value count every time they change then you can do that by saying dollar effect, and then inside there, it logs those values.
JASON: There we go.
RICH: So this is code that will run after the dom has been updated. There's another version that will be updated before the dom is updated if you need that, typically you don't. It allows you to do, I don't know, analytics, you can interact with third party libraries or you can measure the dom to see how large an element is if you need to position something. All of those sorts of tasks you can do in side effects, this is similar to react user effect, we know from the fact that you're reading count and double inside the effect that those are the dependencies.
JASON: Got it, got it, got it. There's a question here about effect, and the question is, could effect do the same thing as derived? And I'm not sure what that means.
RICH: So I think I know where that is coming from. In Svelte 4, we didn't have a separation between derived and effect. It was kind of strange in retrospect, but it sort of felt like this sort of works, let's go with it. In Svelte 3, instead of having this derived Rune, we had something called the dollar legal. You do dollar code on double equals count times two. The compiler would look at that, it would see that everything inside that dollar label was referenced inside the component. You would say, okay, this statement depends on the value of count. So whenever count changes, we'll rerun the statement and reassign that value to double. Then if you had something else that depended on the value of double then it would run after double and so on. So that was how we did derivations in Svelte 3. Well, of course if you're just logging something, you know, dollar count double, even though you're not signing anything, that's still code that depends on those values and it's still code that Svelte is just going to run after those things have changed. And so we had derived and effect with the same syntactical construct, which like not because we planned it that way exactly, it just sort of worked and we went with it. But what we found over time is that that tend to yield confusion, partly because when you have side effects, you generally want those to be after the dom has been updated. You don't want those to happen before the dom has been updated. It's helpful to have a separate thing that says this will happen after the dom has been updated. It's a little bit weird to look at code and be like, oh, this is something that depends on that and this is something that happens in response to that, there's this concept in programming called command query separation, it's one of my favorite pages on the C2 wiki, which is basically, some thing, some function calls in in your app are do something functions and some functions are return something functions. And you mix and match those at your peril. And we were mixing and matching those. And we're now saying, the return something type is the derivation something and the do something opportunity is the -- I think it would be hugely beneficial if instead of saying, you know, total equals add AB, and then console dot log AB, the same syntax for these two completely different concepts, like what if it was run console dot log AB or something like that to force you to differentiate between commands and queries. That's a whole separate tangent. We can't do that, we can't change JavaScript to impose that. But we can change Svelte, so that's what we've done. In line with other frameworks, Solid has create store, create effect, we now have state derive and effect. As a result, components are just a little bit easier to understand.
JASON: So effect is one of those words that it has many permutations across different frameworks. There are a couple of questions in the chat about this. One is, what is the similarities and the differences between use effect and react and effect in Svelte? I noticed for example, the first thing that jumped out at me, I don't have a dependency array. Is that an issue in Svelte? Like, is that a thing I need to think about or care about?
RICH: No. It's all derived from usage. So the list of dependencies can actually change between runs. If you have something like if fu than ba, if fu is false, then you don't care about ba. Ba can change all it likes and as long as fu is false, we don't need to rerun that effect. There's no way to express that concept when you have a dependency array or as in Svelte 3 and 4, when you're determining those dependencies. But if it's determined in run time the way it is in Solid and in Svelte 5, then you don't have that problem.
JASON: Got it. So I think one of the biggest points of confusion for a lot of developers around effects has been -- I mean, I live in a largely React bubble, so I see a lot of confusion around use effect because it's sort of become the function to do everything in it. So when we're looking at Svelte, like if I'm going to do dom interaction, if I'm going to bring green sock in for an animation I'm going to put on my home page, if I'm going to make fetch calls, I'm probably reaching for a use effect in React which can lead to some fairly confusing outcomes, if you don't -- double rendering and things, it can just kind of trick people into thinking their stuff is broken in dev mode, not adding a dependency rate, getting infinite loops. How does Svelte approach the same thing? I know animation is a first class component of Svelte. So maybe that one's not a good example. When we get into writing fetch calls, what's the pattern in Svelte and like how do we avoid some of that confusion that we get where we're kind of using one escape hatch for everything?
RICH: I think some of that is -- I think some of that is intrinsic. As soon as you start using effects, you're kind of breaking out of the declarative paradigm, you're saying, I'm going to decide what happens now instead of letting you control everything. So inevitably, that creates the risk of complexity. There are -- for some of the examples that you gave, there are mitigations. So the infinite loop, for example, Svelte will track infinite loops and will say, this effect keeps running, that's probably not supposed to happen, so you might need to rewrite this code. It will do that in development mode so that you can fix those. For the animation, as you noted, we have animation primitives built in, so you're less likely to use effects in that context. For the fetch thing, this is where the meta frameworks come into play because generally speaking you shouldn't be fetching data inside an effect. Data is something that should be handled outside the component and passed in, or in the React server components world, where you have components running on the server, you have the data fetching inside the component and then you sort of send it to the client in the separate set. We don't have a concept of server components. Instead we have this idea that your data tree is this kind of parallel thing to your component tree that resolves first and then we pass the data into the component. So we can do the async stuff there, your data fetching there. And that way we're not relying on effects which are much harder to orchestrate across multiple components. And so I think the overall takeaway is, effects are extremely necessary. You cannot have a framework that does not have a concept of effects. But we can say this is like special occasion food, right? This is not -- this is not our rice and beans, this is like our chocolate sauce. So you only need it when you've earned it.
JASON: [Laughs] Right, you can't have any effects unless you eat your meat, I get it.
RICH: Actually if we do that component conversion I was talking about before, we'll actually see an example of this. And hopefully I'll be able to explain a little bit better what I'm talking about. The more that you can do with state and derivations and just have things expressed as data flowing through your application, the better a time you're going to have.
JASON: Got it. Yeah, yeah. That definitely makes sense. I think this is also just one of those things that like this is the skill these days, is learning to think in these different modes of like what are you doing inside of your app, when are you working with the framework, when are you stepping outside of the framework and how do you make sure you're not stepping outside of the framework in an unnecessary way, because I think that, at least to me, that seems to be where I see the most pain, people are opting out of the framework way of doing things, and at the level they're doing it, okay, but why are you using a framework at all if you want to do this thing like this, you're just fighting the tools now, if you just want to fight the tools, don't use the tools. So I do -- yeah, I think this is really fascinating. And I'm really excited to see this refactor. If I want to do this component refactor, where should I go to grab that component?
RICH: All right. Let's create a new Svelte app.
JASON: Okay. I'm going to create a new one of these. Close this one down. Make this one bigger. And I'm going to get into here. And -- all right. So I want to create a new SvelteKit project and I do that with MPM create Svelte.
RICH: At latest, in case there's something cached somewhere. You can give it a name now or in the next step.
JASON: And we're going to call this -- let's see. I'm going to call this -- are we going to create a second project for Svelte 5 or are we going to work entirely in this one, do you think?
RICH: We're just going to work in this one.
JASON: Okay. So let's go with Svelte 5 -- I'm just going to call it Svelte 5.
RICH: We'll do the demo app for this. And then up to you if you want to use JavaScript or TypeScript. I tend to use TypeScript when I'm building an app.
JASON: Okay. Let me make this bigger so I can see what's going on.
RICH: Yeah, looks like some stuff's getting missed there. We're not going to add test, we can skip all that. If you go down to the bottom there and hit space, then we can install the Svelte 5.
JASON: So we're going to move in and I'm going to NPM install. Then I'll run the NPM dev. That opens up on 5173, great. All right. I am in SvelteKit.
RICH: I can't remember if we enabled this in the config default. Try doing the command shift over the window and see if that brings up the inspector, if not we'll have to enable it. It doesn't seem to be installed. We'll do that as a first step, because it's something I really love. So go into your Svelte config -- I'm sorry, it's the Vite config. I'm sorry. All my time is spent on Svelte itself these days. Okay. So I was wrong, it's inside the Svelte config. I promise. It will be worth it. And add a new field at the top level. So underneath kit, just add a Vite plug-in, capital P, and then inside that, add a property called inspector true.
JASON: Oh, here. Inspector true. Okay. And then is it going to auto restart for me -- oh, it already died.
RICH: We already killed it. If you refresh that page. Now try the command shift thing.
JASON: Command shift. Am I hitting the right button? I'm not 100% convinced that I'm -- let's see. Is this shift? Yes, that's shift. Command shift. It might be my keyboard but I am not getting the shortcut.
RICH: That is too bad.
JASON: Yeah, I'm not seeing --
RICH: Oh, you know what? I see the error message in the console. This is my bad. This is a thing that works for the plug-in Svelte 4, it does not work yet for Svelte 5, I should have realized that. I can explain what it is and why I get so excited about it. It's this integration that built by Dominic, one of the Svelte core maintainers who is also a big contributor to Vite.
JASON: I've seen this, this is the one where you can click on something and it opens it up in your editor.
RICH: Yes.
JASON: So good.
RICH: You tap on it and it opens it right there and it will edit it, it will go to the very line of the element you're trying to edit. Unless you're using a later version and it doesn't work. Never mind. Anyway. So let's open the counter dot Svelte component. All right. So another thing that doesn't work is HMR. Normally if I was demonstrating SvelteKit to someone, we would try editing a file and saving it and it would magically update in situ. For now we're going to just have to reload the page like cave men. Press those plus and minus buttons a little bit and if you really spam it, you'll see it behaves with some like nice spring physics, essentially. And the way that that is implemented, we can see on the right, we have a piece of state, which is count. And then we have a thing called displayed count which is a spring value.
JASON: Okay.
RICH: And displayed count is something that we call a store. A store is, as I was saying, we have this way of representing state that isn't component state. We use stores for that. And the store is just an object with a subscribe method that gives you the current value. And so on line 7, when we say displayed count set count, what we're saying is whatever the current intermediate value is, like say it's naught point three and we at the time back to zero, it goes back down instead of continuing its upward path to 1. Further down in the component you can see the displayed count is being used somewhere with displaying those digits inside the component. Yeah, and it's translating the whole thing based on the displayed count and one. It's a nice little way to get some spring physics into our component. And then you can see below that, on line 8, we have this offset equals modulo displayed count and one. Line seven, this is what I was talking about before with this distinction between derivations and effects. Line 7 is effectively an effect, we're doing something, displayed count dot set count. Line 8 is a derivation. We're saying whatever the value of displayed count is currently, offset is derived from that.
JASON: And so to let me work through my mental model of this and make sure I understand what's happening, because originally I would have thought both of these were derived values but now that you say it, I understand. I'm going to repeat it back to you. Here we have -- we're setting state. The state is zero. And we can increment or decrement that. We've got it set here. Offset is whatever the difference is between the current value and like whatever is being displayed and the like plus or minus one, right?
RICH: That's used for positioning.
JASON: And this displayed count is an animation value which is something that has to happen after the render. So we are -- we're setting our state. We're determining the derived state and then once we've made the dom updates we're changing the spring value so we can animate between where we were and where we are. That's why this is an effect and this is derived.
RICH: Yes.
JASON: And my poor little brain is broken because they look like the same thing to me [laughs].
RICH: Well, they kind of are. Someone in the chat actually noticed, why can't line 7 just be dollar code on dollar displayed count equals count. And it sort of could be. That would work. You could do that. But it's this kind of weird ambiguous case because the displayed count is not equal to count. When we do dot set count, even though dollar displayed count equals count would compile to the same code we currently have on line 7, it's actually not the same thing. We're not saying the displayed count equals count. We're saying the displayed count is this intermediate value which periodically we give it a new target, and it's constantly trying to make its way towards that target, if that makes sense. It's really more of a side effect than it is a derivation. But it's this kind of ambiguous case. So how do we refactor this into Svelte 5?
JASON: Okay. I think I can refactor part of this on my own, so let me try. I'm going to go with, let offset equal derived. And then I want that to be modulo displayed count one. So that's my first set here. Then I'm going to put these back in order. No, still mad at me. Oh, because we switched to Runes mode.
RICH: We switched to Runes mode. Notice the whole thing, even though we switched to Svelte 4, it still works. You can mix and match those components with new components written in Runes and it's totally fine. You did that totally fine. But there's one missing piece, now that we're in Runes mode, let count equal zero doesn't mean the same thing anymore.
JASON: So we need to go state equals zero. Like that?
RICH: Yes. Hopefully -- if you refresh the page, it will continue to work.
JASON: It sure does. This is good. This to me makes sense because I have -- I've got my displayed count here, like if I was organizing this in my own code base I would probably end up with something that looked kind of like this where I've got my displayed count, I've got my count in state, we're deriving the offset, we are updating the displayed count in effect, we've got this modulo function to help, and now like my brain is less broken here. I am one of those people who I like to see things declared before I use them. So this is how I would organize this code. And this makes sense to me. It's very like, all right, I got it, we're deriving values, we're updating things when the dom changes, like after the dom renders, and now I know -- like now I know what's going on with a little less, like, wait, what is that dollar sign colon do? This feels good.
RICH: There is a bug, though. If you refresh the page and pay close attention, you'll see it.
JASON: Oh, we get a not a number.
RICH: The flash of nan, the Wrath of Khan, the reason that happens is there is a little bit of a difference between how the dollar code on statement works and how effects work. And so if you have a dollar code on enable, that code will run on the server. In Svelte 5, we do things a little bit differently. The derivation will be calculated on the server because obviously we need that in order to server render HTML. Effect is typically something that only needs to run in the browser because it's interacting with a dom element or something like that. So we have this situation where the displayed count is initially undefined. And so the things that you see in the template, it's doing math dot floor and math dot seal on an undefined value, that's why we get nan in the template. We need to initialize our displayed count with the state value.
JASON: Okay. So that means I would go here and I would -- can I just drop it in like that?
RICH: Mm-hmm.
JASON: Okay.
RICH: You can. And it will work now, like if you refresh the page, it will be correctly rendered, but you're getting a warning.
JASON: Did I mean to reference it inside of a closure. Does that mean a closure just like this? No, that's not right.
RICH: That is what it means. But that would give you a worse problem. So this is probably a little overwhelming to someone who is just like, I just want to learn about Runes and everything. But I wanted to bring it up because it's a good encapsulation of the difference in philosophy between Svelte 4 and Svelte 5. We kind of ideally don't want to think about this in terms of effects. As we were saying before with the whole, you can abuse effects and get into a really complicated place, and like is this actually an effective or is this a derivation, it's a little bit ambiguous. We want to be able to express things as far as possible just in terms of state and not overuse effects for this sort of thing. So ideally, the way that we would express this is we would have like a Rune based equivalent of the spring, and do it, initialize it, and pass in a closure that read that value, like displayed count equals spring, and then you would pass in that parens arrow count arrow function that you just created before, and then inside the template you had referenced displayed count dot value. So you would have the read only view onto the in between version of that count. And every time you update count, that updates. But if you're not updating count, then it will just stay at the initial value the whole time. That way you don't have this weird distinction between like derivations and effects. Everything is just data, it's just flowing through naturally, and you wouldn't see that warning anymore, which was put in place to prevent people from accidentally using state in ways that they probably don't intend. Sorry. That was a lot.
JASON: No, that was a lot. And I'm not quite sure I picked up what my action step is here.
RICH: So the action step is wait for us to implement a better way of handling this stuff. Through an there's a kind of impedence match between the state way and the store way and we want to embrace the state-based approach as much as possible.
JASON: Got it, got it, got it. So for the time being, this is a warning, not a bug. It will work, but it can and well, it sounds like, be better.
RICH: Yes. Most of the time if you're referencing state inside the scope where the state is declared, not inside function, then that is state, because you're only going to reference initial value and it's never going to update. If you're not referencing state for its current value, then what you are doing? So generally speaking, if the compiler sees something like that, it says, hey, you might want to check this. I'm going to allow it, I'm not going to stop you from doing this, but you might want to check this. In this context there really isn't an alternative to how we've done this. But the compile certificate saying, this looks a little weird, did you mean to do this? That's because we haven't yet finished building the set of tools that would enable you to build this component in the best possible manner.
JASON: I guess the other way we could solve this, I suppose, is we could just have double magic numbers.
RICH: You absolutely could. Or you could have let initial count equals count although I suppose that would get the same warning. But yes, there are workarounds.
JASON: Yeah. It seems like this is fine, this gets us where we want to go. But for all the reasons you stated, I am excited to see how you encapsulate the Runes philosophy into the motion tools. That's going to be really cool to see.
RICH: Yeah. And when we talk about like having reactivity in the language itself, that little warning there that's telling you that you might want to do that a little differently, that is the kind of thing that I'm talking about. By having this concept of state be in the language, we're able to understand the intent of your code to a much greater degree than we otherwise could. And because of that, the compiler is able to say, oh, hey, I think this is probably supposed to be done a little bit differently. And this is just one example of us leveraging that idea. I expect we'll find many more over time. And I'm just like really interested and excited to see what it will mean to have these concepts built into the language.
JASON: Yes. Yeah, I think that's going to be extremely cool. Okay. So we're in a project, we are writing code. One thing that I would love to see is, now that we're in SvelteKit -- are we in SvelteKit?
RICH: We are.
JASON: Okay. So now that we're in SvelteKit, I had talked about what's the right way, quote, unquote, to like fetch data in an app, to load things in. Can we look at that?
RICH: We absolutely can. Maybe if we look at the Sverdle app, that would be a good way to get a handle on this.
JASON: Here's the Sverdle app. Oh, it almost fits. I can't spell.
RICH: What about learn, since this is "Learn with Jason." Right letter, wrong place.
JASON: I'm not going to burn time actually playing this. If you haven't seen Wordle, you just guess letters, guess words. This is great. Okay. So this is cool. And then to load this, do I go to Sverdle? Can I take a little detour before we talk about Sverdle itself? Where did the plusses and stuff come from in file name? I ask because I think route -- like file-based routing is probably one of my favorite things I've seen in frameworks and I love it so much. And I just haven't seen this convention before. So what -- like what led to the choice to put the plusses in? Because for somebody who is not in the Svelte world, this feels really different compared to what else I've seen.
RICH: Yeah. It's something that we spent a lot of time figuring out the right way to do this. For a while we had index dot Svelte convention. And if you had -- so you see there's an about there, we have a plus page Svelte about there. You would have index dot Svelte and about Svelte. The index dot Svelte would be slash route and the about dot Svelte would be the slash about route. And nice and intuitive, great, cool, whatever. But what happens when you have a component like that counter component, does that become slash capital C counter? How do you differentiate between root components, root files, and the components consumed by those root files? We started having elaborate rules, maybe if it has a leading underscore it doesn't become root, or we have a way of saying these files are private, these files are public. It actually doesn't scale all that well. So we were kind of aware of this problem, although we weren't actively looking to solve it until the next team shared their vision of how the at rooter was going to work. So they have a similar concept where roots are defined by directories rather than files and inside those directories they have page dot JSX and like an error dot JSX and all of these different files have like special names that allow you to have loading screens defined declaratively, your components defined declaratively, and it solved all these problems in a really obvious way. But I don't think it solved it all the way.
JASON: Because if I have a component called error, it clashes.
RICH: Yes, but even if you know all of the special names, then you're probably going to have some files in that directory which are used somewhere in that root, which like Lexically are between the special files. So you have an error page and you have -- sorry, you have the error.JSX and the page.JSX, maybe in the middle you have help us dot JSX, common utilities. Now you've got your special root files down here and your own stuff in the middle. It's really hard to see at a glance which files have special meaning to the framework and which files don't. When we do it this way, all of the special root files, they're very visible, at a began you can immediately see what's special. If you look at that and don't know what it is, we've given you something very obvious to search for in the SvelteKit docs, it's super easy to learn what plus page means because you can search for it in the SvelteKit docs. If you do the command P things, just type plus P, you see your roots right there in front of you. So you can see at a glance without any special dev tooling or anything fancy, like it's just there in front of you because we're using this very common convention. Honestly, when we first announced we were going to do this, people went mad, they were so angry at us, they were like, what are you doing, this is such an inexplicable decision. Then people used it for a while and almost everyone, there are still a few holdouts, but almost everyone was like, oh, I get it, I love this.
JASON: I will say my -- like it's very much like a who moved my cheese moment, where if you're not familiar with that, it's this idea that like decisions that don't matter, like moving somebody's cheese, will send them through the roof because it's like an unexpected change to your environment and for whatever reason that makes humans just like absolutely clamp down, like I hate this, don't touch it. But I didn't have the rationale, I saw it and said, that's interesting, I never saw that before, I don't know if I like that. But in your explanation you did a similar thing a while back that instantly changed my mind so you instantly changed my mind on this plus convention, I was like, that feels a little extra, and now that I know why you did it, it makes sense. You did the same thing with tabs versus spaces, maybe a year ago you put up a tweet that was like the reasoning for using tabs over spaces for accessibility concerns, for screen reader concerns, and it makes literally no difference because it's a prettier config setting, and I was like, oh, of course I should use tabs, like why wouldn't I use tabs, that's a ridiculous thing for me to care about. But prior to that that I was like, I'm going to use spaces, because screw you, everybody, I like spaces [laughs]. So anyways, I guess you've now done that twice in your career, where you have instantly changed my mind on something that I thought I had a strong opinion on.
RICH: Great. The tabs versus spaces, a real hobby horse of mine, I hope that's the default one of these days. It's Robert, of course none of this matters in the coming AI revolution. Absolutely right.
JASON: Hands up if Rich convinced you to use tabs. Me. Okay. So with our remaining time, because we don't have a ton of it, I do want to get some data in. So we were looking at Sverdle, let me do the plus page on it. We're in Sverdle.
RICH: On line 7 we have something called export let, in Svelte 4 that's how you declare it, in Svelte 5 it's a little different. Our component is essentially a function and we call it with some data that gets rendered. And that data is provided by the framework. And the framework gets it from your loader. So every plus page dot Svelte file can have a sibling file called plus page dot JS or plus page dot TS. The difference between them is your load function in a plus page dot JS or TS will run on the server and on the client. Your plus page dot server dot JS or TS will only run on the server and it will send that data to the client so that it can be used interactively.
JASON: So whatever the return value of load is will be available in that data prompt.
RICH: Exactly.
JASON: Gotcha. Okay. If I want to do a fetch, I would run it right here and I would return the response as part of my load return value.
RICH: You would, although the fetch specifically, because fetch is a browser API and it has context like the current origin, it behaves a little bit differently on the server. People may have tried doing a fetch with a relative path or beginning with a slash and that doesn't work in the server context because you don't have an origin.
JASON: Right.
RICH: To make it a little bit easier to do things with fetch, we actually provide a version of fetch that is optimized for use with SvelteKit. So where you see cookies in the load arguments -- sorry, in the parameter, add a fetch, that is how you would do some fetching.
JASON: Okay. So I can -- can I make this async?
RICH: You absolutely can.
JASON: And we're going to hit -- what is the actual -- it's the dog CEO API and they have a random. Here is our random image, what is that, go away, then we're going to get this. And I'm going to await my random dog. What did I just do? So then we'll -- we're not going to do air handling. We'll just get data.
RICH: There's a syntax error, the async needs to be after the yellow parenthesis.
JASON: Oh, I see, are we doing this as a --
RICH: The reason for that is we have the status, we have to wrap the whole thing --
JASON: Got it. Dog image is going to be data, message I think is what comes back, and we're just going to roll with that in like the most simple way that I can, and then here I've got data, so right up at the top of my template, I can add an image of a dog, I think. So let's try it. I'm going to say data dot dog image, is that what I called it? Random dog. Did I get it right on the first try? Let's find out. I did. I'm a genius. You're all welcome. No, but so now every time we load, we're going to get a different dog. Okay. Well, I mean, that is like brutally simple, I love that. That's beautiful, that that just works like that. So anything you want to do, you just do. And I assume I can use the regular fetch API if I want, or should I always use the Svelte fetch?
RICH: You can. You can use the regular fetch. There's a couple of differences between the provided fetch and the native fetch which are documented on kit dot Svelte dot dev. For something like this, it's essentially equivalent.
JASON: This is wonderful, look at that little guy. This is great. This makes a lot of sense to me. I feel like especially if we go back to the counter, this -- it makes sense to my brain that has no muscle memory for Svelte. And I feel like that's such a good sign, when it feels like it's part of the -- like the platform model of thinking about things and not like -- you know, because the Svelte dollar colon is kind of like how when we used to have all the React life cycle methods, it's like not hard but it's a lot of stuff you have to learn about exactly how the thing does each thing. But if we're starting to think in terms of a unified model, signals are basically becoming the default unit of state across frameworks, and there's the state, there's derived values, there are -- or derived or computing depending on if you're using viewer or Svelte or whatever, and then we have effects. If we can get our head around those concepts, we can move between frameworks and build great things based on our needs as opposed to based on which thing we know well enough that we can navigate it.
RICH: That's a really good encapsulation of what Svelte is about in the first place. We're trying to build the framework for people who don't give a shit around frameworks. My career background, I come from newsrooms where a lot of people writing code are not doing so because they're excited about writing code, they're doing so because that is the thing that you have to do to get the end result that you want. And so having a framework that is like as self-explanatory as possible so you don't have to refer to the documentation or become a framework expert is something we put a high premium on. It's also an encapsulation of what Svelte 5 is specifically all about. There are certain aspects of Svelte that have not that Svelte-like, if I'm honest. We haven't even talked about some of the other new features that have been added to the Svelte 5. We had a conference last weekend, there's a video, there's docs, we can talk about that another time. But essentially we're trying to pare the idea of a framework down to the barest essentials, trying to remove all the complexity, all the places where you need to have framework-specific knowledge. And we're never going to be able to get to zero framework-specific knowledge, you'll always have to have some knowledge of the thing you're using. But we want that to be so small that you can learn Svelte in an hour. And if you do that and pick it up in a month's time, you'll still know how to use it. That is our sort of north star with this stuff.
JASON: I love it.
RICH: So far it's going pretty well, I feel good about our current direction. We're adding features but we're reducing the learning curve, it's pretty rare that you get to do that.
JASON: That is really exciting. I wish I had more time. But unfortunately we are out of time. So I'm going to bring us home. I just dropped another link to the preview into the chat. Let me bring this up onscreen for anybody who is not able to see the chat. Also I'm going to bring up another link to you here. Go find Rich on GitHub, and there's links here to everywhere else that you would need to go. And I'm going to do one more shoutout to our sponsors, Netlify, making the live captioning work, thank you to Lee for being here from White Coat doing the live captioning. Check the "Learn with Jason" schedule, we've got an absolute killer schedule coming up to close out the year. We'll do some emails with JSX, we'll learn about identities, we're going to have a lot of fun. Make sure you subscribe so that you get the updates of when things are happening, when things are live, and whenever we add something new. Rich, anything you want to send people off toward, any resources I didn't share, anywhere you want them to check out or just some parting thoughts before we close down for the day?
RICH: Yeah. If you like Svelte, if you want to explore Svelte more, then our Discord is the place to go, you'll get an invitation to join our Discord on Svelte.dev/chat. Svelte Society is a sister organization to Svelte, there's also a YouTube channel. And there's always good stuff happening, the Svelte Society is all over.
JASON: Collection links there. Awesome. Rich, thanks so much. This was an absolute blast. I hope to have you back soon, because it is always so much fun talking to you about this stuff. But until next time, thank you all for hanging out, and we'll see you next week. Later, y'all.
RICH: Bye.