Qwik 1.0 is unlike any JS framework you've used before
Now that Qwik has reached a stable v1 release, let’s take another look at what how Qwik’s resumability and other innovations impact how we build for the web. Creator Miško Hevery returns to teach us.
Links & Resources
Click to expand the full 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 LENGSTORF: Hello, everyone. And welcome to another episode of Learn With Jason. Today, on the show, we're bringing back Mi�ko Hevery. Mi�ko, how are you doing?
MI�KO HEVERY: I'm doing so good. Thank you for having me again.
JASON LENGSTORF: I'm superexcited to have you back. I've been looking forward to this one because we're talking about Qwik today, and all the neat things about that. Let's talk a little bit about you. For folks who aren't familiar, can you give us a bit of background on yourself?
MI�KO HEVERY: I'm known for this thing called Angular and I've joined Builder.io. I'm working as a CTO. We're working on Party Town.
JASON LENGSTORF: Very, very cool. Yeah, so, I [No audio] it's got a lot of cool stuff. We've done a couple episodes on it. We've talked a lot about the concept of resumability. We won't go into resumability, itself, if you're interested in that, go to the previous episode.
A couple things have changed. One, Qwik 1.0 is out. So, Qwik is now kind of beyond the experimental phase. It's not stable. We're 1.0, we're ready to rock. When people talk about Qwik, I've heard the conversation be along the lines of, you know, this idea of resumability is so cool, Qwik is so cool, but I need it to have x, z and z. When I've talked to you, you've said, that's actually not true. Qwik already has these things.
MI�KO HEVERY: First of all, your mic's doing the thing again. I'm sorry to distract you here.
JASON LENGSTORF: Crap.
MI�KO HEVERY: I'm sure our listeners will have an issue as well. While you're debugging, I'm going to entertain people.
One thing that I want to get across here is Qwik is productionready. That means that it's not just a framework, we have the meta framework to go with it. We have all the pieces you need for styling, image optimization, end points, everything you need to kind of get going.
So, yeah, let's talk about that. Let's see how we can build something interesting. I was thinking we could build a app and add styling through Panda CSS and all the basic things we need to get it going.
What Qwik is doing is Qwik is taking that same input, with� it's a JSXstyle input and you don't have to execute the stuff twice. Anything that can be serverside rendered is serverside rendered. All the browser has to do is know you are interacting with it. So, it's similar in spirit to what something like Astro is doing, but finegrained because with Astro, you're dualexecuting the bits of frameworks that you're hydrating. With Qwik, you never actually do it.
How does that sound?
MI�KO HEVERY: Your mic is still acting up, here and there.
JASON LENGSTORF: The chat's not hearing it. I think it's our connection, specifically, that's doing that and I apologize.
Qwik is designed in the same exact way, out of the box, we just want to be performant. There is no best practices or 10 tricks you need to do to make your app run faster. We want to basically say that, like, look, build your app the way you're used to build your app. The easy way should be the performant way. We're saying that it is the framework's responsibility to make sure applications are fast.
When the application is slow, people point fingers and be like, oh [No audio] and I want to kind of flip this around and say, like, no, if you developers are doing a bad job, it's fundamentally a problem with the framework so I want the framework to take on the responsibility.
MI�KO HEVERY: I do, yeah. [Laughter]. It's a good analogy. I would just phrase it differently. It's not like there's a big button that says, "make this app slow," there is a default and you have to make it fast. Be like, no, the default should be fast. You should not have to do any extra work to make it faster. You shouldn't do any minimization and prune the tree. The framework should be designed in the way where the right thing happens without any effort on your part.
JASON LENGSTORF: Absolutely, yeah. That is really good� you should set good defaults. Make a default that is easy for people to follow and then, like, don't get� like, when possible, don't even make it an option to do the wrong thing. Especially if there's no reason why you should ever do the wrong thing, adding that control is just a temptation that lets somebody go in and make a mess when there's really no good reason to make that mess.
Everybody's going to say, well, I have this specific use case that requires this or this or this and as a project maintainer� talking to Rich Harris about this was interesting. In Svelte, they have had to limit what they accept in the API. If you introduce something into your framework, you don't get the option to pull it out without massive churn and we're feeling that with Next.js because they changed a bunch of their APIs. It points to the thoughtfulness required when you're introducing something new.
And why it's so important to get those defaults right, from the getgo, it's easy not to add things in the first place. [Laughter].
MI�KO HEVERY: You think it's easy. It's quite hard. It always looks like a good idea and you want to add it and later on, you realize, actually, there are nuances to things that I didn't realize or whatever, right.
JASON LENGSTORF: That's totally fair.
MI�KO HEVERY: I totally agree with the fact that adding things should be considered. Building the primitives rather than the actual thing. One of the things we want to make sure is that on the primitive level, the framework enables the right stuff and other people can build on top of it. The primitives, we can do code extraction and lazyloading and prefetching and analysis to make sure that the code is already there for you so the user never has to wait. Lots of tricks that you, the developer, could do to make the app fast. But that is not your job, your job is to make an interesting application.
JASON LENGSTORF: That was one of the things that really jumped out to me about Qwik. You casually mentioned that Qwik is capable of serializing nonserializing things. We've spent a ton of time, as a community, working around the issues of not being able to serialize a function. And so, the fact that that just happens, it's in the box. You don't have to ever think about it or really worry about what's going to trip you up, as you said, it gets out of your way. It lets you focus on building something valuable instead of wrestling with the boilerplate of the app.
MI�KO HEVERY: Yep. Should we build something?
JASON LENGSTORF: Let's do it. Let's build something. I'm going to switch us over into the pair programming view and I'm going to do a quick shoutout to our sponsors. We have Vanessa here, from White Coat Captioning, making this closed caption button on Twitch work. We are open for more sponsors.
Okay. We are talking to Mi�ko and that is on� is Twitter the right place to highlight? We'll throw a Twitter link out.
Yeah. Okay. Thank you for the raid. What's up? I see you are in with friends, thank you all, very much. Welcome.
All right, y'all, let's talk about Qwik. So, I'm going to open up the Qwik home page. I'm going to drop that here. And you are going to have to tell me what to do next.
MI�KO HEVERY: All right. So, grab the NPM and go to your terminal, paste it in.
JASON LENGSTORF: Let's open up one of these...and...just start it from here. We'll go to my GitHub folder, into Learn With Jason, and then I'm going to run one of these. Too many arguments?
MI�KO HEVERY: Make the window a little bit bigger.
JASON LENGSTORF: So, this is� that's cool. [Laughter]. All right. So, we are creating a new project. We are going to create this project in Qwik 1.0 and Beyond, why not?
MI�KO HEVERY: I like it.
JASON LENGSTORF: All right.
MI�KO HEVERY: So, it's asking to do a basic app, which is just basically a blank page. Or we could start with a visual CMS. I don't want to delve too much in the CMS, but it is a cool demo. Maybe we spend five minutes showing that off and then we can go and build something.
JASON LENGSTORF: Okay. [Laughter].
MI�KO HEVERY: Did you not take the joke.
JASON LENGSTORF: I did take the joke. Why do Java programmers wear glasses? Because they don't C#. [Laughter]. Oh, boy.
MI�KO HEVERY: It's a little bit of me, inside of that. I like bad jokes. [Laughter]. Bad dad jokes. I keep my dad jokes in the dadabase. [Laughter].
JASON LENGSTORF: Ohhh, got. That's� all right. Here we go.
I don't even know where to go from here.
Okay. So, what I really like about this, too, is I like that when we finish, it says "success." It says how to start the app and then, it also, like, walks us through, did you know that you can add integrations, like, Netlify, Cloudflare, when you create a new project, this is good. It's very helpful. And, I'm glad that we're seeing this makes its way into the Quick Start templates to get everything set up and not say, here's your project, good luck. But here's what to do next, here's how to get help.
That being said, let me actually open this window. So, I'm going to jump over into...ah, crap, did I put this in the� I did. Okay. We're just going to move this out...you know what? I'll move it later. Okay. [Laughter]. So, here is� here's our folder. We've got Source. So, the Readme's got all the pieces in it. We've got a packet JSON with scripts preconfigured. Not a single dependencies.
MI�KO HEVERY: That's one of the things we care deeply about, we shouldn't have external stuff unnecessarily. So, yeah, let's get it going, right. What was the command? NPM Start?
JASON LENGSTORF: Yes, it was. I just realized� chat let us know I got our names backwards.
MI�KO HEVERY: I was enjoying being Jason for a while. [Laughter].
JASON LENGSTORF: Well, I'm glad you enjoyed it.
MI�KO HEVERY: Oh, you don't want to be me?
JASON LENGSTORF: I meant� I apologize, came out wrong. [Laughter]. No, okay. So, yeah, this is� this is� this is cool. All right. We're ready. You said the start command was "NPM run start. "I'm going to hit one of those and it's going to open up in 5173...
MI�KO HEVERY: So� as I said, I don't want to spend too much time with Visual Studios� CMS. It's a cool process. One of the things that has to happen is we have to get a public key for you and now it's like, hey, you don't have a public key, let's get a public key so it's taking you through the process.
JASON LENGSTORF: Oh, nice.
MI�KO HEVERY: I believe you can sign in with your Google Account.
JASON LENGSTORF: I believe I already have one because I talked to Steve before. Looks like I used a different email. Oh, it's going to make me do all of these, okay. I don't know what I am. Here we go. Authorize. Oh, that's cool. Did it all that fast.
MI�KO HEVERY: Yeah. So, now you have a� basically, a page, and it's integrated with Visual Studio so it's showing you where the different components are coming from and you can hand it over to the marketing people and say, hey, you edit stuff. If you click on "open," then you should have kind of an editor in here and you can draganddrop things. I don't want to spend necessarily too much time in here because I think people want to talk about Qwik. It shows the level of integration we're looking for, just making it supersimple to get it going and explore things.
JASON LENGSTORF: Yeah. And this is, like� this is great. How fast is that? And then I will head back to here, refresh the page...
MI�KO HEVERY: I think it might take a couple seconds for the caches to invalid. But it should be less than a minute.
JASON LENGSTORF: Cool. Okay. So, we got it. We're in here. We're set up and we're ready to rock and roll. We got a Qwik site running. Should we look at the source code, where do you want to start?
MI�KO HEVERY: I think we should start with a brandnew route. You have a REST API you can fetch. Let's go to the Source folder and there are Routes and inside of the Routes, we have a Catch All, that sends everything to our CMS. Make a new folder, call it� I don't know what your REST API does, call it whatever's appropriate.
Inside of the Episodes, you do index.tsx.
JASON LENGSTORF: To show this API, the Learn With Jason API has a REST end point that will give us episode details. So it's going to pull everything that's published on the site and give us back, you know, whatever we need, some links and URIs and all those bits so that we can show things on the page.
MI�KO HEVERY: Perfect. I love it. This is� this is excellent. So, I think the first thing you want to do is make a component. You can do qcomponent, it should autocomplete and there should be a template. There you go. That's not the one. Uhhhhh...
JASON LENGSTORF: I think I might not have� so, this is an abbreviation, which is going to give me the wrong thing. Do I need to install a VS Code plugin?
MI�KO HEVERY: When you installed Qwik, it should have also set up a�.vscode folder.
JASON LENGSTORF: It's got it, Recommendations.
MI�KO HEVERY: There's snippets. It should be working.
JASON LENGSTORF: Let me close and open again because my VS Code setup is absolute chaos so it is entirely possible that I have this in a [Indiscernible] way. So, let's go with...
MI�KO HEVERY: Nope. Still not there, huh?
JASON LENGSTORF: This is definitely my VS Code set up because every week, I install five, new things in VS Code. [Laughter].
MI�KO HEVERY: We can do "Export Default Component $." This returns a JSX. Make a function�
JASON LENGSTORF: This is the function?
MI�KO HEVERY: Yep. That's right. And then just return, you know, div, "hello world," or whatever you want. Or, "episode."
JASON LENGSTORF: If I run this again� because I restarted my server...
MI�KO HEVERY: Now, if you go to the/episodes on your browser, it should say "hello world."
JASON LENGSTORF: Okay. So we'll go in here. I'm going to make this bigger and� oops. I'll edit this...Episodes. We got this and it picked up our layout, which is nice.
MI�KO HEVERY: There's an existing layout for the starter that you can edit. So, let's just try to build something. So, the thing you'll probably want to do is go fetch data from the server. And there's different frameworks that have different frameworks of doing it. We like this idea of loaders. We can go into a discussion of why loaders are a good idea, they're used by Remix and Solid. Let's make a loader. Export const, use episodes.
JASON LENGSTORF: You don't call it a loader?
MI�KO HEVERY: Use Episodes and I'll say "route loader."
JASON LENGSTORF: Route loader.
MI�KO HEVERY: Create an Async function and it will return your data.
JASON LENGSTORF: So, this equals "wait fetch." And we will get "HTTPS, v2, episodes." And we shouldn't need anything other than that.
MI�KO HEVERY: You have to do JSON.
JASON LENGSTORF: We'll do "if okay," then we will� otherwise, we will get episodes, wait, res JSON and then we can return episodes. And do I have to put it in an object or return it, straightup?
MI�KO HEVERY: You might want to give it a type. I believe Episode is "any." We can do a simplified version of it. You don't have to do the whole thing.
JASON LENGSTORF: I can copypaste it out of here, I think. Here, here...here. And then, I think if I get my episode [Indiscernible]. I think that's everything. So...so this is going to give me back, um...episode, as array. I think. And I didn't break anything. Haha!
MI�KO HEVERY: Let's fetch the data. So, the way you get ahold of it is say "const episodes equals use episodes."
JASON LENGSTORF: Nice. Okay. I understand.
MI�KO HEVERY: When you hover over Episodes, it has the correct type. A couple of things are happening here. First of all, you can have as many loaders as you want. You can have as many as you want. Second difference is that because it's a wellknown name, you're not allowed to refer to it because you will create� you will break the treeshaking. Because you're not allowed to refer to it, the types don't automatically flow, instead you have to manually say, oh, I got this loader data, I think it's of this type but nobody's checking you that you're doing the right thing. So the nice thing here is the type information flows correctly. And the other difference here is that when you use a loader, because it's a wellknown thing, wellknown export, you can only get a hold of the loader data in a specific component, which is the component for that particular route.
Whereas here, you're welcome to use the "use" method anywhere. You can use it deep down or wherever you want. Common thing to do is to create a "use" method, a loader, that tells you what the logged in user is and you can use it anywhere you want inside of your component.
JASON LENGSTORF: Very cool. I'm just kind of getting us to a�
MI�KO HEVERY: Yeah, I know. Thank you. Thank you for doing that while I was chatting. Clearly, you've done this before. You're a professional here. [Laughter].
JASON LENGSTORF: So, this is giving me a void, which leads me to believe I need to doublecheck that it's set?
MI�KO HEVERY: So if you hover over Episode, it should tell you what type it is. The "episodes.value."
JASON LENGSTORF: "Type void is not assignable to JSX children."
MI�KO HEVERY: Oh, don't put a curly. It doesn't return anything.
JASON LENGSTORF: That's what I'm doing wrong.
MI�KO HEVERY: Line 67 also needs the curly removed.
JASON LENGSTORF: All right. So, now it's not mad at me anymore, but this is mad at me because I did this wrong.
MI�KO HEVERY: I think it's complaining about a key because it's a loop. If you have an ID, you should use the ID. If you don't, you can use an index. That's it. Now if we go to that particular location, it should all be there. Look at that.
JASON LENGSTORF: Look at it go. Supercool. And it feels like� this is intuitive. I was trying to force some of my existing knowledge, which is why I kept trying to run ahead of you. I like this idea of you specifically name what your function is so that we could have 20 loaders if we wanted to.
One of the things I've noticed that people do that gets them in trouble, they need to load comments, et cetera, and it's not superslow, but takes time. And you run all of them and they don't know to parallelize it so you end up with a� you know� threefoursecond load because you serialized all of these calls. Are these loaders being automatically parallelized?
MI�KO HEVERY: They are. They run in parallel. There is a hierarchy. You can have a loader on a layout, so the layout has to run before the other loader because it can log the user in and provide the user information to the other loaders. But, yeah, at a same level, they are essentially all parallelizable. Even if they're not parallelizable, because they're on different levels, like, layout runs before the route loader, there are ways to explicitly to parallelize it. It's an advanced concept. You can do a lot of stuff.
JASON LENGSTORF: That's� that's really cool. I'm� little bit of inside baseball here. As that question started coming out of my mouth, I was like, I really hope this is a softball and not forcing Mi�ko to explain why something is done. I knew if something is done already, it's y'all. [Laughter].
MI�KO HEVERY: We're very proud of this. We looked at a lot of other solutions in this particular space and we think loaders are the right way of doing this.
JASON LENGSTORF: This is going to show us Vite. That's in dev mode. Pretend it's not there. If I go to the filter, I can say "minus Vite." This is a Chrome extension.
MI�KO HEVERY: If you were in an incognito window, you wouldn't have that there.
If you jump into Elements, you will see there's a couple of script tags there. There is one called "Qwik JSON." And that's equivalent� for those of you coming from Next.js, that is like _data. Notice it's empty, there's nothing in there. But if you were in a Next� Next system, the Next data would have all the JSON it loaded because it was a "get server props." And that JSON needs to be serialized because the client needs to run hydration and it needs that data to be able to revalidate and reconcile. The hydration will produce nothing because there's no interactivity. Soon, we will have interactivity.
[Audio cutting in and out].
JASON LENGSTORF: Okay. You just started freezing up on me, real quick there. You're coming back.
MI�KO HEVERY: [Audio cutting in and out].
JASON LENGSTORF: Okay. I think we're good. I'm checking on, like� looks like my connection is still okay. Okay. Still with me. You look like you're back.
MI�KO HEVERY: All right. Yes, yes. So, I think what we should do is we should make it interactive. I think that's the next step.
JASON LENGSTORF: Yeah, let's do it.
MI�KO HEVERY: I don't know what you normally do, what I like to do is add, like, a filter, a box that represents the filter and as I type in it, I want to shrink the list of items I see on a page. Let's add a box.
JASON LENGSTORF: What I can do is, I have tags. So, I can� tags.map, and then we'll do a tag and for each of these, we will return just a span. I can [Indiscernible] key, that'll be the tag, I guess. The tag slug, because that should be unique. And we'll show the tag.label. I forgot to close out my...oh, boy. Mic is in a very in convenient spot, where I can't see some of my keys and so it leads to me� okay. So, these are� these will be our� ah, let's do it like this...
MI�KO HEVERY: I think if you do�
JASON LENGSTORF: That's doing the whole thing. So I need to do it like this, then.
MI�KO HEVERY: Correct, yes.
JASON LENGSTORF: One of these and one of these. Okay. That'll give us an actual list and then up at the top, we want to add some filters, so we'll add� do you want to add it here or add it as a separate component?
MI�KO HEVERY: This is fine. Create an input with a placeholder that says "filter" or whatever.
JASON LENGSTORF: We'll do one of these.
MI�KO HEVERY: Do we want to do the work on the server? It's not necessary, but you do you.
JASON LENGSTORF: We can do it all clientside so why don't we keep it simple.
MI�KO HEVERY: You don't have to give it a name. It's going to be all clientside. You might want to put a placeholder in there, and that's it.
So now we need to store the value of the text somewhere, right. So we need essentially state. In Qwik, we call it a Signal. Say "const filter equals use signal" and create an empty string.
So what we want to do now is we want to bind� you could do the value trick, onchange and all that stuff. You can say filter and we do the stuff underneath it. So now what we have is, you can go back to the output, you now have a search box and the search box actually works. You can type into it. Of course, nothing's going to happen because we didn't do anything with it. But if you go to the serialized state, you'll find we didn't actually serialize anything. We did interactivity and there's a listener, the Qwik JSON is still empty. It's not empty, it has a filter in it. What is not in here is the big JSON that you worked with before because it's unnecessary.
Maybe to make the point kind of clear, you should print out the bound value so people can see it's actually interactive, right. Below it say, sure, filter.value. I guess it's not strictly necessary. You can see it's working, it's interactive. But the system looked at it and said, I got all this data from the server, but the data isn't ever going to be rerendered and I don't need it, so it gets thrown away. The framework is automatically optimizing for you without you even having to think about it.
Now what we would like to do is create a filtered list of episodes. And so, now we probably want to say "filtered episodes equals use computed."
JASON LENGSTORF: Oh, we can use "use computed." Oh, nice.
MI�KO HEVERY: There you go. In here, just put a function that basically does the right stuff.
JASON LENGSTORF: So, then what we will do...episodes.filter. We can take the episode, itself, and we will say "episode� epi.tags.label starts with� can we do "contains"? "Includes"? Oh, no. Oh.
MI�KO HEVERY: Show me how you filtered. Did you do value over there? Sorry. Episodes.value. It's episodes.value. Line 56, episodes.value.
JASON LENGSTORF: And this is also an array. I have to do episodes.tags� can you do a partial match inside of an array?
MI�KO HEVERY: Don't know. [Laughter].
JASON LENGSTORF: So this is going to be a little bit of a�
MI�KO HEVERY: You can do text.
JASON LENGSTORF: It is just on text. We'll just� we'll just take all of these and we'll say�
MI�KO HEVERY: Take the tags and join them. Join the tags using� and then just check to see if the join contains the string.
JASON LENGSTORF: Oh, yeah. So, we'll join.
MI�KO HEVERY: Index of not equal to 1.
JASON LENGSTORF: So now�
MI�KO HEVERY: Filter.value.
JASON LENGSTORF: Filter.value. Okay. And then we can switch this out for�
MI�KO HEVERY: I think that's negative one, I think, no? Not equal to negative one?
All right. So, if we did this right, it is filtering but my filter logic is broken so that means...that what I am doing...is...goofy somehow.
MI�KO HEVERY: So, is the tags� does it contain an object or a string?
JASON LENGSTORF: Oooohhh, crap! Yep. Okay. So, we're� I think we need to do it the way that I was doing it. Um�
MI�KO HEVERY: You would have to map it first.
JASON LENGSTORF: Yeah. So, okay. We will go with tag and return t.label and then we'll join those. Then, that should give us...
MI�KO HEVERY: Is it case sensitive?
JASON LENGSTORF: It is case sensitive.
MI�KO HEVERY: There you go.
JASON LENGSTORF: We did it, mostly. This is, like, the worst filter of all time. If we type in one of our pieces, it starts showing things. If we type in, you know, Qwik, there it is. So, it's, um, it's good. It's doing a lot of what we want here. And we could also do more. We could, like, match titles and stuff like that. But this is good enough for our purposes, I think.
MI�KO HEVERY: Go to Qwik JSON. Notice that now this thing is huge because it contains the full JSON. We could talk about tricks in here. But the think I want to point out here, you made an application and you're basically saying, I need to rerun through this list so the system was like, I don't have a choice, I need to serialize this. Go back to the source code and go back to where the� where you were iterating, what was the line? On the bottom somewhere? Line 74. Instead of Filtered Episodes, go back to Episodes. Now if you go back to your application, you know, the filtering's not going to work because you're not doing the right stuff. But the Qwik JSON's going to be there because the computed is running, right. It doesn't know that nobody's listening so you have to comment out the computed. If you comment out the computed, right, this is now what will remove the JSON from the output.
So now if you look at the quick JSON, it's going to be much, much shorter.
JASON LENGSTORF: Yep.
I don't want to go into it deeply because we went into it previously. I want to talk about the other capabilities. We created a route loader. Typically� maybe the other thing we want to create is an endpoint. Let's make an endpoint. Maybe it should return an "okay" or something. Let's not make it complicated. You can make the endpoint, in this particular route, if you want, but you will have to decide, do I return the HTML or the endpoint? So it might be easier to create a new route.
JASON LENGSTORF: Sure. We'll call it "API" and I'll do an index.ts or index.tsg?
MI�KO HEVERY: .TS. You need a method called "onget." So, "export const onget." And handler. And it's an async. You can put curly braces around and say "JSON." And then you can just say, in here, you just say "JSON 200." And pass in an object, like "okay," or something.
And so now if we go to� what's it complaining about it? Sorry, don't return it.
JASON LENGSTORF: Oh, don't return it.
MI�KO HEVERY: Yeah.
JASON LENGSTORF: Oh, okay. Okay.
MI�KO HEVERY: You can have chainings. If you go to the URL, you'll have this thing. Now we created an endpoint and you could go fetch the episodes or whatever. Your imagination is key here.
I want to point out how easy it was. You wanted a particular route, you wanted an endpoint. You can have middleware, it wraps things together for authentication. We could have a middleware where everything gets invoked and if you're not authenticated, it will send a message.
JASON LENGSTORF: What are the limitations on stuff like middleware, does that run as a� I know that on Netlify and Vercel, do you figure out how to make it work with whatever adapter?
MI�KO HEVERY: You right the middleware, you don't have to think about it. You have a consistent way of writing your logic and then, you know, you deploy� sorry� to Azure or Netlify or whatever you want.
We can go to the layout. So, inside of the root of the route, there's a layout.tsx. We can add a middleware to prove a point. Create an "on request" function.
JASON LENGSTORF: Handler. Basic function...
MI�KO HEVERY: And just do consolelog and print the current URL.
JASON LENGSTORF: So is that going to be right there.
MI�KO HEVERY: Request.url, I think. Yeah, there we go. And now whether you go to the previous route or the endpoint, you'll see that the URL's going to be printed because that middleware's running in front of everything else.
JASON LENGSTORF: Uhhuh. So we could do something, for example, if we had a pricing page and we're going to load prices, we could, you know, hit� hit the edge to see where's this person loading from and update the pricing based on their location. If you're doing purchasing power parity or localize the content, we could hit our translation API. It'll execute on the server side so on, like, on an Edge function, whatever server's hosting it. We don't need to care, we need to write the middleware and it'll for us.
MI�KO HEVERY: That's right. Let's go to our example with the episodes, right. Inside of a component, let's just say "console log rendering episodes." Inside of the component. So that's the route loader. Not inside the route loader, inside the component, itself. A route loader always executes on the server, right. So you can do file/system/API. It's a serveronly thing. Component, on the other hand, might execute on a client or a server. It says "rendering component." It is executed on the server. If you interact with it on the client, it� okay, you know what? I think we're doing too many things in this particular example. [Laughter]. Can we� let's see. Let's just� can we just comment out the list of episodes? Or, actually, yeah, you know what, let's put the list of episodes in a separate component. Let's do that.
JASON LENGSTORF: Okay. Can I do it as a subcomponent, like this, or do I need to put a new file?
MI�KO HEVERY: Like this, "const episodes equals component $."
JASON LENGSTORF: I guess I need to do it as one of these.
MI�KO HEVERY: Keep the filter outside. Take the "ul" part. In here, we're going to leave behind episodes, right. And pass in episodes� say� yeah. And then whatever you want� yeah. Okay.
JASON LENGSTORF: Here, I have my component and then props come through, same as I would expect, right?
MI�KO HEVERY: Yep.
JASON LENGSTORF: Okay.
MI�KO HEVERY: And then just paste that in. There put an angle bracket and say it's an interface, it's a curly, episodes� you don't have to say the word "interface." Just say the word "curly" and say "episodes:" It's the Episodes array, right? And then you can get rid of the value. It's still a signal, but typewise, it doesn't look like a signal. Put the value there.
JASON LENGSTORF: Okay. All right. It's not yelling at me anymore.
MI�KO HEVERY: Where you put down console log render component, say "render default component." And over there, it would say," render episodes," right?
JASON LENGSTORF: Okay.
MI�KO HEVERY: So, on a server, we see both, server component, episode component. Let's go to the client and start interacting with it and see what happens. So, notice the Episodes is rerendering, but the default one isn't and this should be a surprise because in most systems when you mutate a state, that's the component that gets rerendered. The state is tied to the component. In Qwik, the state isn't tied to the component, it's only there for convenience reasons, but it's actually not tied to it so when you go and interact with it, you don't have to download it. You don't have to execute it.
If you go to the Network tab, what you're going to see� let's refresh it and go interact with it and see Downloads. If you type something inside of the filter box, let's go through what downloaded. The first thing, if you click on the first file that downloads.
JASON LENGSTORF: Chrome extension. We'll skip that one.
MI�KO HEVERY: Look at the response. And so, this is basically your bind: Value. The next bit that downloads is the framework, itself. You can kind of ignore that. That's the Qwik, basically, itself. There's Build, you can ignore that, as well. Notice the next bit that downloaded is the "use compute it." Because when you type, now the system realized, oh, I need to recompute "use computed" because that is now invalidated.
And let's go to the next bit. And so the next bit is this is the component [audio cutting out] it is just the episodes component. It is not all the other stuff in the file. Right.
JASON LENGSTORF: You're kind of putting these out� even though I did� I treated this like a singlefile component, in the compiler, you're breaking these out so that they can be fullytreeshaken.
MI�KO HEVERY: That's right. The default component actually never makes it into the client because there's no code path that causes the component to rerender. Only the Episodes needs to get rerendered.
MI�KO HEVERY: The thing I wanted to point out, the default component executed only [Indiscernible] and the Episode component executed in both places. If the default component had� in this particular case, we don't have components don't go away. If you had a UI, where the component would actually go away, then you would have a situation where the component would get created oninit but it's ondestroy gets run on the client. So it spans server and client. As a developer, you don't think about, is this running in a server or on a client? Where is it. It's running wherever it's convenient at the moment. Now, certain things, like route loaders, they can only run on the server so they're permanently tied to the server. But other things basically run when it's convenient, wherever it is convenient.
JASON LENGSTORF: That is one of the things I think is good to just clarify. In a lot of frameworks, there's sort of this magic runtime where you write code all in the same place and sometimes it's running in Node and sometimes it's running in the browser and that's usually awesome until you hit a case where you import a Node module that can't run in the browser. How does Qwik approach that? If I bring in a browseronly API, is� is Qwik going to explode when I run it in the server context or if I bring in the Node Crypto Module, is that going to explode in the browser?
MI�KO HEVERY: Let's make a simple button and answer that particular question. Inside of your default component, let's make a button and let's call it...I don't know "awesome," inside of JSX.
JASON LENGSTORF: I'm going to put this, up at the top, so we can find it easily.
MI�KO HEVERY: And give it an onclick so that we can click on it. So, it's onclick and $, and then arrow function. But let's make it a multiline error function, let's put it in console.log. So, clicking can only happen on the client, so by definition, you know the onclick only works with the client. If you click on it, it says "clicked." Let's make another console log below it and consolelog "expensive." Okay. And so, now let's say that the expensive part, you want to execute somewhere else, let's say we want to execute it on the server. Let's turn the expensive into a function and call the function directly in line.
JASON LENGSTORF: Okay.
MI�KO HEVERY: And so now, below that, just say, you know, "function invoke it." So far, we haven't done anything here, yet, right? And so, maybe� let's make the function� let's make it that we call it probabilistically. It is greater than 0.5, then call the function.
JASON LENGSTORF: 0.5, then we will call this function.
MI�KO HEVERY: Okay. And so now if you click on it, sometimes it's going to work; sometimes it's not going to work. Now, let's say you want to lazyload the function only when it's called so when it's not called, don't load it. So, go ahead and wrap the line� not the line, the function. Not the�
JASON LENGSTORF: Oh, I get you. Out here.
MI�KO HEVERY: For some reason, VS Code refuses to imports dollar signs, I have to file a bug with them. It correctly inputs every other dollar sign, just not the one by itself. [Laughter]. Now, let's click on the "awesome" button.
JASON LENGSTORF: All right. So, I'm just going to pull this back up. So, we have� just to make sure this is clear. We've imported $, which we have not talked about what that does yet and used that to wrap this function. There's a random chance of calling that function. Okay. So, first flip, called it; second flip, didn't.
MI�KO HEVERY: Let's refresh it again. The first time you click, I don't want it to call "expensive." Refresh the page until it doesn't. Go to the Network tab. Let's go to the first thing we downloaded, the client.
JASON LENGSTORF: The client is�
MI�KO HEVERY: Notice the function, on line 5, it got replaced with a dynamic import so what we have just done is we made a particular function to lazyload for ourselves. Right. And, then you can call it on line� what is it, line 10. It is asynchronous. Notice if you look at any other file that's being downloaded, the function actually is not present in any of those files. We didn't download it yet and so now let's click on the button again, there we go. Now it showed up. So, you have just created a lazyloaded piece of code without any sort of effort. Like, the effort is wrapped in the dollar sign, right. That's pretty cool, isn't it?
JASON LENGSTORF: That's extremely cool. And is it smart enough� if I do something like, let's say I want to import UUID?
MI�KO HEVERY: The import will go to the function. So, the import goes with the function.
JASON LENGSTORF: Do I import it in here?
MI�KO HEVERY: No, you put it on top and when the import gets extracted�
JASON LENGSTORF: So, this came out of the crypto module, which is, if I remember correctly, a nodeonly module. That doesn't run in the browser.
MI�KO HEVERY: That's going to blow up. Let's fix it. Ready? Go change the dollar sign to "server $." I want this function to always run on a server.
JASON LENGSTORF: There's no way it's that easy.
MI�KO HEVERY: It is that easy.
JASON LENGSTORF: I screwed something up. Does not provide an export name server QRL. Did I screw�
MI�KO HEVERY: You messed up the import. It's from the Qwik City.
JASON LENGSTORF: Oh, got it. So let me...get rid of that one [audio cutting out]. There it is. Here's Quick City and now...
MI�KO HEVERY: Now notice that the Expensive part executes on a server.
JASON LENGSTORF: Look at it go. So, it's happening over here. Oh, that is really cool.
MI�KO HEVERY: So this is what we mean by unified execution model. As a developer, don't think of it as a server and a client. Think of it as a single thing.
JASON LENGSTORF: Hold up, hold up, I got to try one more thing because now you got my brain going here. [Laughter]. I want to pass in my filter, right. And we're just going to make this, like, whatever. And then I want to�
MI�KO HEVERY: Just print it, yeah.
JASON LENGSTORF: Output that. Oh, it needs to be the value. So then, are we� so, it's going to�
MI�KO HEVERY: Type something into the filter.
JASON LENGSTORF: Oh, my god. It just works.
MI�KO HEVERY: It's even better. Go to line 98 and remove the argument. Don't pass in the filter, remove that. Just directly say filter.value there.
JASON LENGSTORF: Get out of town! Oh, my god. Just� get out. [Laughter]. Like, this is� I mean, this is so, freaking cool. This is the sort of thing that is not� it is so hard to do this if it's not built in because it� it requires these mental gymnastics of, I'm on the client, I'm on the server, how do I get this value from the client back to the server and now, hold on, because we can take this even one step further. Let's return� we'll say "filter is awesome." And then here, I'm going to... you have to wait?
MI�KO HEVERY: Yeah. Because it's now async, right. You have to put "async" at the top. Get rid of the randomness. Because at this point, we don't need it.
JASON LENGSTORF: Good call. So, now...
MI�KO HEVERY: Okay. You want to have your mind even more blown?
JASON LENGSTORF: I don't think I can handle it, Mi�ko. [Laughter].
MI�KO HEVERY: How about instead of returning the value, how about you return a function that you then have to call in the client. In other words, the server creates it, but you have to wrap it in the dollar sign. So...yeah. Do that.
JASON LENGSTORF: Okay.
MI�KO HEVERY: It's just saying�
JASON LENGSTORF: Okay. That's fine. So, then we'll get...
MI�KO HEVERY: By the way, somebody talked about using it in React. It's not going to work because you need to wrap line 102 inside of the $.
JASON LENGSTORF: We have to make it a lazyloadable thing.
MI�KO HEVERY: You have to import it. I'm sure they're going to fix it, at some point.
Yeah, so now, basically what just happened is you executed a function that executed on a server, then server created a closure that can close over variables and then you get the response on the� you get the function on the client that you can then call. Right. So, in other words, now, the server can decide what code lazyloads into the client.
JASON LENGSTORF: Absolutely� just, like� just bonkers. Bonkers that this works. Here's the thing� okay. So, we're playing right now. Right. We're taking stuff ask seeing how it works. Imagine how this allows you to do so much more here. So, if you've got a server function that needs access to an API key, but it's part of an onboarding flow where you need user input, you can do this seamless handoff where you get some user input, do some stuff that needs an API key and pass functions back and forth the whole time so you can query a function� you probably have to be careful here, the API key's going to show up on the client when you pass it back. You got to be aware of what you're actually doing here. But, holy shit. [Laughter].
MI�KO HEVERY: Yeah, "holy shit" is a good way of putting it. [Laughter].
JASON LENGSTORF: It's just incredible how seamless that it is and how nice that feels use.
MI�KO HEVERY: Unified execution model. Think of the server and the client as a single virtual machine and you do your work and the right thing will happen.
Somebody in the comment section said it's the same thing as Use Server, that React has. I want to address it a little bit. I might be paraphrasing and I apologize if I get this wrong. But, Use Server is very, very different because it doesn't allow you, for example, to automatically capture closers. You have to specifically pass values and they have to be JSONserializable objects. You can't� for example, in our case, on line 100, we're just capturing the filter. Like, we [Indiscernible] capturing the filter and that becomes available on a server.
JASON LENGSTORF: Yeah, I mean, so, to just reiterate that, from the blown mind of a, like, okay developer like me, we're, like, jumping through context here. When you make this into a server function, this server function is being captured and moved outside of the source code, which means it should lose its reference to this because it's no longer in the same file where this value is declared. However, Qwik, in the compiler, knows that there's a reference to this value and is able to then pull that out in a way that keeps it live because this value can change at any time in� in the client, and the server needs to keep its valid reference to that live.
[Multiple people speaking at once].
MI�KO HEVERY: That's a place where it gets a copy, it doesn't get liveupdated.
JASON LENGSTORF: It does, doesn't it?
MI�KO HEVERY: If the server keeps a reference to that signal, it's no longer connected.
JASON LENGSTORF: As far as my mental model goes, it does because what I'm expecting is when I update this filter, my server function can maintain its live link to whatever this value is.
MI�KO HEVERY: From that point of view, yes.
JASON LENGSTORF: I don't know how I would do that in another framework, where you have a� like, a signal on the client side and you've got something that you can only execute on the server� I would find myself reaching for an API endpoint to is send that signal back to my functions, which would then return a value, which I would have to figure out how to turn back into a function. Can you do all these things in other frameworks, yes? Is it easy? Sometimes very much not.
Unified execution, where I'm writing code. I tell it where I want it to run and it just works and I don't have to think about how you hopscotch this value. That's really freaking cool.
MI�KO HEVERY: It is. I think so, too. So, the thing that people don't realize is the dollar sign that we have inside of Qwik, that's unique to us and it is our superpower. It's not necessarily easy to explain why all of these things become power but basically, this dollar sign is what allows you to do all of these ridiculous things. We have a Worker Dollar, I'm not sure if it landed in manual yet. If you say "worker dollar," you are running stuff.
I want to show you something else. How much time do we have?
JASON LENGSTORF: We have, like, 10 minutes.
MI�KO HEVERY: I'm excited about Panda.CSS. I'm going to talk about it, let's not doing anything because we don't have time. For those people who like Emotion, Panda CSS, you put your styling directly in line inside of your JSX. Emotion has performance issues and also serverside rendering complexities that Panda CSS doesn't have. You get benefits of Emotions, but it's atomic CSS like [Indiscernible] and everything happens at build time, not at runtime so you don't have a runtime performance impact with it so it's a really, really nice combination of things. So, something to go and check out.
Since we are only down to 10 minutes, I want to show how we do images. So, can you grab an image, a favorite image you have, maybe a big image, and drop it inside a folder.
JASON LENGSTORF: Let's open this image in another tab. This is large, this is very large so I'll copy the image address.
MI�KO HEVERY: You want to download it and put it inside of the Routes folder.
JASON LENGSTORF: It's not premium, I got to find another one. We will save as.
MI�KO HEVERY: There should be a folder called "Media." Source. Let's put it inside the Source folder for now.
JASON LENGSTORF: Okay.
MI�KO HEVERY: Okay. So, now, let's say we want to use it�
JASON LENGSTORF: Do I need to go to JPEG?
MI�KO HEVERY: It should be fine. Let's go to this source code and you should be able to import the image through, like, Import Image, you know, from, and then just give it a name.
JASON LENGSTORF: Okay. So, we'll say, from...
MI�KO HEVERY: Uhhhh� and so, a couple of things. Can we add a "?JSX" at the end? And call it "burger," like, with a capital B.
JASON LENGSTORF: Let's drop it right at the top here.
MI�KO HEVERY: Okay. Let's see if this works. It worked. Perfect.
JASON LENGSTORF: How do I do Alt text?
MI�KO HEVERY: You can do it over there.
Is this Chrome Tools?
JASON LENGSTORF: This is Art. Chromium, so it's Art.
MI�KO HEVERY: If you look at the image, it has source set, different sizes so the whole thing got automatically optimized for you. I'm going superfast through this because I don't have time. All the right stuff has already happened. What it should be doing is [Indiscernible] there's an accumulative layout shift because you didn't say what the image size was and so I'm not quite sure why that is not happening. We can debug this later. This is the Builder CMS. You can go to the burger and give it a width and a height of something more reasonable.
JASON LENGSTORF: We'll say 300.
MI�KO HEVERY: The browser will select the correct downsampled image for you. I think, let's see� oh, because it uses source set. I think you're supposed to do it through a style of max width, or something like that. I need to get better at doing these tricks so that I can do better demos. [Laughter]. I think because it's a style sheet, you need to do "px pixels."
JASON LENGSTORF: Got it. There� we get� we get the correct size and if I add it as HTML, we've got all the different source sets and everything. This is looking great.
MI�KO HEVERY: Um, and this is part of the Build tool so you can� on top of it� use other technologies, like, for example, there is Unpic, by Netlify, and Unpic lets you use other peoples' CDN systems. This is the Build system, itself.
I wanted to show you that all these things are handled for you, out of the box. It should have screamed at you saying, hey, you forgot to do width and height and there's a button that pops up and it changes your source code and adds the width and the height for it.
JASON LENGSTORF: Oh, cool. I do think my VS Code is dysfunctional, to say the least. At some point, I should probably uninstall it and install it. [Laughter].
MI�KO HEVERY: No worries. We're out of time. Hopefully I was able to blow your mind a little bit today. The point of this was not necessarily to talk about how Qwik does things, but really to show off that we have all the things you need to build production site, endpoints, image optimizations, routes, everything you need.
JASON LENGSTORF: If anybody does have questions, I'm going to point them out the Qwik Dev Twitter. Where else should someone go if they are excited, they want to learn more?
MI�KO HEVERY: From our home page, you can find Discord chat link and this is where all the action is happening so go into Discord and, you know, ask away. We have lots of, what we call, community heroes, which are people who go around and answer everybody's questions. They're doing such a wonderful job. That's a place to be.
JASON LENGSTORF: Excellent. Well, what a� what a [Indiscernible] of an episode. I don't think I've got any brain left.
It's really exciting to see what's happening in this space. I love the push toward, like, performance and optimization as a default state of being and the drive to, like, make something that lets developers focus on the task athand instead of trying to wrestling their tools into a state.
Make sure you go and check this out. Go give this a try. Somebody in the comments saying, this is a techno interview for me, you're right. And I don't think I passed. [Laughter]. But, yeah. So, let me do another shoutout. We've had live captioning, Vanessa, from White Coat Captioning. That is made possible through Netlify and Vets Who Code.
Check the schedule, we've got really, really fun stuff coming up the rest of the month. We're going to learn about TypeSafe and JS. These are going to be really fun and useful. Make your calendars and head over to the Learn With Jason Discord so you can join in, have a good time.
Mi�ko, any parting words? Anything you want to say to folks?
MI�KO HEVERY: People oftentimes come and look at Qwik and they're like, oh, it looks just like React. I think the big thing to realize is that Qwik intentionally makes the DX looks like something you already love. That's not an accident. The valueadd of Qwik is not a cooler DX, even though we think our DX is amazing. I think the value of Qwik is what happens under the hood, how it runs and how it executes and how it actually enables a better UX without any sort of effort on the part of the developer so I think it's a very much a paradigm shift in how applications get built without actually changing how you actually build them.
JASON LENGSTORF: Got it. What a great way to continually help people onboard. Use what you know, make it a little bit better, by default, and ship awesome things for the web.
We're going to go find somebody to raid. So, stay tuned for whatever comes next and we'll see y'all next time.
MI�KO HEVERY: Thanks for having me.
Closed captioning and more are made possible by our sponsors: