Let’s Learn Waku for React
with Daishi Kato
Waku’s goal is to “make React development fun again”. Learn how this minimal framework lets us build with React Server Components and more with creator Daishi Kato.
Topics
Resources & Links
- https://twitter.com/dai_shi
- https://waku.gg/
- https://github.com/dai-shi/waku
- https://66da437451bc25c01df3d2f6--rad-kataifi-cabed9.netlify.app/
- https://waku.gg/#deployment
- https://github.com/dai-shi/waku/tree/main
- https://www.learnwithjason.dev/schedule/
- https://lwj.dev/discord
- https://t.co/cJ1WMvRaoX
- https://github.com/pmndrs/zustand
- https://github.com/pmndrs/jotai
- https://github.com/pmndrs/valtio
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. Today we're coming to you at a special time because we are doing extreme time zones this morning. Let's see, this morning for our guest. This afternoon for me. I don't know what time it is for you. But today we're going to learn about a new React framework. I'm really excited about it. It has the ability to do a lot of things that I think have been considered very, very advanced but is making it hopefully approachable for lots of people. The official slogan of the framework is "let's make React fun again," which is in line with my values. Here to teach us is the framework creator, Daishi Kato. Thank you for joining us. How are you?
DAISHI: Hi. Good morning, everyone. I'm fine.
JASON: It is grade to have you here. Thank you for taking some time. I know you had to wake up early, and I really appreciate that. For folks who maybe aren't familiar with you and don't know your work, could you give us a bit of background on yourself?
DAISHI: Yeah, my name is Daishi Kato. I'm an open-source developer, which is currently my primary work. My projects include three-state management libraries for React and Waku, the minimal React framework. A couple days ago, I wrote a short blog post about my journey with open source. My very first open source contribution was like more than 20 years ago. So yeah, if you are interested, you can check it out.
JASON: Yeah, you'll have to send a link to that, and we'll make sure it goes in the show notes. So you contribute to three state management frameworks. One of them I know is (indiscernible). And what are the others?
DAISHI: All from the same GitHub organization.
JASON: Got it, got it. Yeah, and I've heard a lot of really good things about those. I've heard -- my friend Jack Harrington has talked about Waku and really likes it. I've seen chatter generally about Waku itself. So at a high level, you know, I said already that on the homepage, Waku says let's make writing React fun again, which I love. What is Waku? Like, why did you create it?
DAISHI: Yeah, so there's a bit of history. Actually, there are two aspects. I'll explain one by one. The first one is this is my research project. It's an experimental project because we cannot use React server components without any frameworks. We need something. So my research question was what does it look like if you are to create something very minimal that only allows us to run server components. So the other aspect is as this gets some traction, we have some requests that can -- can we use this? Can we use this for production? So we started to add more features. It's still minimal, but now it's more like a real framework. So two aspects. One is research project, experiment to explore capabilities. The other is to provide a minimalist framework that people can actually use.
JASON: Excellent. Yeah, and I know that you wanted to spend most of the time coding. I'm also really excited to get into the coding on this. So, I'm actually going to just go right into it because I want to keep us moving here. So let's dive over into our pair programming mode. I'm going to turn on this banner. I'm also going to turn on this one for a second because this episode is being made possible through the support of our sponsors. We've got live captioning being done by Rachel. Rachel is with White Coat Captioning. That's made possible through the support of Netlify. Thank you for covering that. And Daishi and I are using Tuple today for pair programming. That's why you can see two mouse cursors flying around here. That is through the support of Tuple. Thank you very much, Tuple, for making this possible. It helps me make more of this show, and I appreciate it a whole lot. So we are talking to Daishi today. Here is a link to your homepage here. So you can go follow Daishi on Twitter. We're talking today about Waku. So, this is the homepage for Waku. I just want to call out, first and foremost, this is a beautiful homepage. It's such an interesting departure from most framework homepages. I just like it so much. It has such a -- it's got a feel.
DAISHI: Somebody said this is like a game.
JASON: Oh, yeah. (Laughter)
DAISHI: The very first page.
JASON: Yeah, this is really fun. So for somebody who wants to get started, what should my first step be?
DAISHI: Yeah, as I said, we have plenty of time today. Let's start with the one aspect which is the research aspect. It's not like some do, but we learn how Waku works and how React server components work. So there's no docs in this webpage, but I can tell you. Maybe we open up a terminal.
JASON: Okay. Let me open up a VS Code window. Okay. I'm ready.
DAISHI: So, it's empty, right? Npm create Waku at latest. Minus, minus, and again minus, minus choose.
JASON: Okay.
DAISHI: Yep. Enter.
JASON: All right. We'll call this let's learn Waku.
DAISHI: And choose 20 minimal.
JASON: Okay. So let me move into this project, and we'll close up this one. I'm going to maximize this to start so that we have -- actually --
DAISHI: I call it research mode, but this is actually for folks who develop libraries. So this is for library authors.
JASON: Got it.
DAISHI: Okay. Let's -- I can explain a little bit. So there are two entry points. There's main TSX and entry TSX. Because React framework requires server or something like server, there are two entry points. If you open up a main.tsx, this is the same file as Vite. So this is a client entry point.
JASON: Okay. I just realized I forgot to install dependencies. I'm getting that done right now.
DAISHI: So main.tsx is a client entry point. So the browser first loads this file. On the other hand, entries.tsx is the entry file for the server end.
JASON: Okay. Sorry. I'm wondering why it's not seeing my --
DAISHI: I don't know.
JASON: Let me just try reloading this one more time. My VS Code server is a little whacky because I change stuff on it all the time. It's fine now. Okay. I don't have anymore red squigglies. So main.tsx is the client entry. Entries.tsx is --
DAISHI: Server entry.
JASON: Got it.
DAISHI: To make it simple, let's comment out from line 13 to 26
JASON: Okay.
DAISHI: So there's just one simple function left. This is what I call render entries. It takes an input, and it returns an object that includes JSX.
JASON: Okay.
DAISHI: So the goal here is that, you know, a way to serialize React components. So the server's job is to receive requests and return the response.
JASON: Right.
DAISHI: And this is the minimal API I came up with, which takes -- input is actually a string. It takes on a string. It's like a path, and it returns components. The reason we have a wrapping object is we may return multiple components in one response.
JASON: Got it.
DAISHI: So yeah, this is everything. So this minimal mode only allows you -- allows this usage. It gets input, it returns output. If you open up again the main TSX file, it has two special components from Waku/client, root and slot. Root is something that you put in the route. And slot you can place anywhere, and the ID specifies the key of the object that we specify in entries.tsx. It's confusing because we key up everywhere.
JASON: So this one here, this ID of app is going to use this key here. Is that correct?
DAISHI: Exactly.
JASON: Okay.
DAISHI: We can put many slots if you want, but this is the simplest.
JASON: So when you say we could return multiple elements in this object, what that practically means is that I could have -- if I had a bunch of markup here, like a header and a side bar and a user section, I could just put in slots for my user section, for my main app, for my side bar, and then each one of those would be pulled out of the entries.
DAISHI: That is correct.
JASON: Got it. Okay. All right. That makes sense.
DAISHI: So, let's see how TSX is complemented. This is confusing. If you look at this file, it doesn't say anything about a server component or a client component. But the framework knows it's our server component because it's from entries.tsx. We import this file from entries.tsx.
JASON: Got it. That makes sense.
DAISHI: And this one is pretty simple, except that we have some suspense thing. But let's forget about it for now. So we have headers and counter. A counter is imported in line three, which -- yeah, let's open up the counter. It uses client directive. It says this ones on client, not on the server. So we can use state and other hooks.
JASON: Okay.
DAISHI: Maybe it's time to run this up.
JASON: Yeah, let's do it. So I have it running at local host 3000. And here is our running app.
DAISHI: Exactly. And can you open up the network tab?
JASON: I can. Let me make this full size here. Okay. And reloading.
DAISHI: Yes. Yeah, local host is the first request, which returns an empty HTML body. It has many scripts, but it's empty. It's a SPA. We don't enable server yet. So it's empty. We load the client entry point, which is main.tsx, at line 52.
JASON: Got it.
DAISHI: That one is a Vite one, so it's not the one we're doing.
JASON: Oh, oh. Sorry.
DAISHI: I think the second one, the third one. No, this is not. Can you go down a little bit?
JASON: Umm... this one is from Waku. This is client.
DAISHI: Maybe down.
JASON: We've got some Waku web sockets.
DAISHI: Maybe I was wrong. It was the first one. The main.
JASON: The main.
DAISHI: Yeah, this is the one we have. It's compiled.
JASON: Oh, I got it. Okay.
DAISHI: What I like to see is the index.text down below in the network. So this is the response from entries.tsx. So remember, it's a function that takes an input string and returns an object with JSX. So if we interpret it, it's a bit hard, but it has -- let's see. Yeah, at line number three, it's an object.
JASON: Yeah, and we can see the name of app.
DAISHI: It's debug information. Line number -- which one? Oh, eight. It's the first one. App is the ID for the slot. And $1 is pointing to line number four, which points to -- no, no, no. Wait. Line number seven. That's the $1. It has JSX serialized.
JASON: Oh, right. Okay. So we've got our HTML and all the pieces that are inside of that app component.
DAISHI: Exactly. And as we have client component, which is counter, which is linked from line number two. So later in the network, it loads counter.tsx.
JASON: We can see counter.tsx right here. This is the -- you know, it's processed by Vite, but otherwise just kind of loading in.
DAISHI: Exactly.
JASON: Great. Okay. And this is the spec for React server components, right? Like, the serialization that's happening here, this is what the RSE spec says to do.
DAISHI: Yes, but they don't specify how we put it on wire. So the way we do it is Waku spec.
JASON: Got it. Okay. That makes sense. And so then, this being our minimal setup here, we have the server component, which then brings in a client component. So if I do something like this part, we have access to client interactivity, but if we were to bring in a counter and mark it as server, it would render a button, but the button wouldn't do anything. Like the use state wouldn't work.
DAISHI: Right.
JASON: Got it. Okay. So I think in the abstract, this make sense. I feel like the core building blocks are there. What do you want to look at next?
DAISHI: Okay. Let's look up something we ignored before. It's still the basic behavior. In app.tsx, we have a suspense boundary. Server message is below, which is simply wait two seconds and return hello.
JASON: Oh, I didn't look at that. Oh!
DAISHI: So if we load it, it waits for two seconds. If you remove suspense boundary, it will wait two seconds for the entire page.
JASON: Got it. Okay. So this gives us a lot of control then when we're working here on, like, I don't want to show my page until this data is loaded. So just wait. Or we can show a loading indicator and the page can load without it.
DAISHI: Absolutely.
JASON: Okay. That makes sense. Let me undo that change so we get our suspense back. Great. Okay. So this is -- I mean, it's all making sense so far.
DAISHI: Okay. One other thing we want to try is to see how -- we saw in the network tab how RFC is returned, but we can try that on TOML. So can you go back to the network tab again?
JASON: Okay. Let me actually get this open. Okay. And this one.
DAISHI: For this index.txt, I think you can copy the URL from the left. And you go to terminal and type call this URL.
JASON: Okay. So curl --
DAISHI: Yeah, it's waiting to seconds. You see?
JASON: Ohh. Interesting. Okay. And so this bit came back later. So let's do that one more time. All right. Did everybody see that, how it gets to here and hangs that two seconds? I'll do it one more time. Got it. Okay. So it's actually the text file implementing this. So is this text file being sent over a stream?
DAISHI: Yes.
JASON: Ohh.
DAISHI: I mean, it's an HTTP stream.
JASON: Very cool. Okay. All right. That makes sense.
DAISHI: All right. What do we do now? Let's play a little bit. Let's see. Just to make sure we can receive the input -- let me see. Open main.tsx, and root component actually receives a prop called initial input.
JASON: Okay.
DAISHI: Let's say Jason. I'm not sure if you need to restore the server, but can you go back to the browser.
JASON: Okay. Then would this show up in the -- oh, here it is.
DAISHI: Yeah, actually, the input is kind of URL. So, yeah. What you typed for the input becomes URL, which then becomes the input for the entries.tsx file.
JASON: Oh, okay.
DAISHI: So just to remind it, open the editor and see entries.tsx. So now input is JSON instead of empty string. And we pass it to the name prop in the app component.
JASON: Right. Let's see. Here's the name. It gets used here. So then when we come out here, it's using that initial prop. Okay. All right. I'm with you.
DAISHI: Yeah, that's pretty much it. What would you like to see from here for this minimal one?
JASON: So the things I'm really interested in are the -- I understand the division of server and client. I know why people want to do that. What has been challenging is in the React server component's model is the -- how to keep things connected properly so that you're not accidentally leaking server side things into the client side. So a little bit more on how we're building the server components. Then if people have questions about what we're doing with RFCs or things you want to see us try, please drop those in the comments so we can make that work. And, let's see. Go ahead, sorry.
DAISHI: That's kind of a tough question because you're asking like a best practice, which is a bit out of the scope of this experimental project. So let me continue with something else for now.
JASON: Okay.
DAISHI: Remember, we comment the out -- okay, before going into it, we can actually see -- if hot reload works. So you want to see the browser and the VS Code at the same time. Let's go to the file counter.tsx.
JASON: Okay.
DAISHI: Anything you modify will be showing up.
JASON: All right. There's our hot module reload working.
DAISHI: Which is the Vite capability, not something I did. But for the server side, let's open app.tsx. The next tab, yes. You can also make some modification. I didn't call it hot module replacement. It's hot reload. It runs behind the scenes.
JASON: Yeah, and does that do it with -- like, if I've got state in here and I go and make a change, it's reloading the whole page. So it'll dump client state.
DAISHI: Oh, I'm not sure why. I was expecting it to work.
JASON: Let's see if I'm doing something wrong.
DAISHI: Maybe not. Or maybe not for this mode. But that's kind of the goal.
JASON: Cool. Okay. I got you.
DAISHI: So now let's go back to entries.tsx and enable get build config. Line 14. So now you are making production. We build it for production. So the information I need is -- yeah, we need to build it. For now, there is only one path name, but technically, it can be multiple pages. So we need to provide the pages. That's how this get build config works.
JASON: Got it.
DAISHI: But for now, it creates the index page. The index page has entries, which takes the input with an empty string. It doesn't say anything, but this is required. Render entry function above doesn't tell anything. It only runs if we provide input. So go to the TOML and type npm run build.
JASON: Okay. So we've got our build. Then we've got our build out here.
DAISHI: In dist.
JASON: I'm in dist.
DAISHI: Public is for the client. It should look familiar. And there are some other libraries. So now we can run it with npm start.
JASON: Okay. Npm start. Custom not found page. Let's see.
DAISHI: This is something I was actually working on recently. You can comment out line 14 again.
JASON: Okay. And rebuild it?
DAISHI: Yeah, exactly.
JASON: Okay. So now this is built.
DAISHI: Yes, and open the network tab. Now it should be quite clean.
JASON: A lot less noise. We don't have any of the Vite stuff, right. So we have our local host here, which gives us far fewer of those runtime scripts. We get this Waku client import.
DAISHI: Yeah, there's one script and some module preload. Again, the Jason text there.
JASON: Much simpler.
DAISHI: Because it doesn't have debugging information.
JASON: So we end up with the server data, our app that serialized JSON. Here's the reference to the countercomponent.
DAISHI: Exactly.
JASON: It's like kind of melting my brain because I don't read serialized code very often, but I can still parse what this is. I understand what's happening.
DAISHI: Right. Okay. So this is what it is. Let's go back to -- let's do something that is not related to RFC but people often want, SSR. So originally, when I started, I wanted to make a framework that doesn't include SSR. But as I understand, if you were to claim a react framework, we need SSR. So eventually, I implemented this feature, which was tough, but it's there now. So, let's see. Now we can run it again. Maybe you can do dev mode first. Npm run dev.
JASON: All right. So let me get back to 3000. Okay.
DAISHI: And it looks the same. Because it's the same. But if you open -- oh.
JASON: Didn't like something.
DAISHI: I think it's fine. It's an issue, but we don't know how to solve it yet. It's working. So you open network tab again. Now for the first request.
JASON: Network tab. We get our first request. And I'll make this big again.
DAISHI: Should include the HTML in the body.
JASON: Yeah, so there's everything. Here's our placeholder for the server text.
DAISHI: This is called streaming. React streaming. React SSR streaming.
JASON: Okay.
DAISHI: The reason I was hesitant about this is because we are basically sending two -- we are basically sending twice. If you look up a bit, we have -- let's see. This is a server component. Neat. But we need to hydrate. So we need to send JSX again down below.
JASON: Right.
DAISHI: I remember, I can use the pointer.
JASON: So somewhere down here is going to be our --
DAISHI: H3. So we are sending this string twice. Once in the HTML tag.
JASON: And this is a problem with every SSR approach. You can't -- well, every SSR approach except Quick and maybe Whiz at Google. In order to SSR, you have to render once, send all of the data, and then also send the client side JavaScript and all of its data so you can, you know, have a -- in case the JavaScript doesn't work, here's the content. Then when the JavaScript loads, rehydrate and rebuild this whole page as a JavaScript page.
DAISHI: The good news is because previously, without server components, we need to send old JavaScript. With RFC, we don't need to send JavaScript. It's just a piece of data.
JASON: The serialized bit here.
DAISHI: So it's further than before.
JASON: True, true. So this is good. We're making progress here. Then is this streaming in? I don't know if I can click in fast enough to see it. This isn't here at the beginning.
DAISHI: This is streaming.
JASON: Got it. Very cool. So, okay. Somebody is asking how do you build a component out of this, taking this part here and then reversing that into a component itself.
DAISHI: On the client.
JASON: I think you said earlier this is like the Waku spec.
DAISHI: Welt if you have a stream or string of this payload, it's then the React spec. It means it's deserialized. React has a capability to deserialize this string payload into the actual React component. So to answer, one is that it's a React capability. So it's done by React. But Waku calls this function. So we have a code that takes this response and calls React function and then render on the screen.
JASON: Got it. And that's sort of if you've seen -- to the person asking this question, if you've seen how when you take JSX, if you use react without JSX, and I'm not going to try to Google that right now because I have no idea where to find it. But if you look at how to build a React page without enabling JSX, it's a series of nested functions. So it's sort of the same idea where like JSX is an abstraction on top of the JavaScript that makes a JSX call, that renders things on the page. So any serialization is just kind of agreeing on a spec that says in order to send this in React's case over the wire, we're going to take it into this plain text format that follows these rules. Then our parser can read the plain text format and turn that into, you know, functioning client-side components. Yeah, like solid's h or fragment. Under the hood, that's what JSX is doing. The same thing. It's sort of a parser more or less.
DAISHI: Yeah.
JASON: Okay.
DAISHI: I think we have kind of done for this experimental mode. Okay. One last thing is that -- so the reason I made this is that I want -- this is a bit of history. I wanted this minimal layer to use React server components. I didn't want to add anything more. So the motivation behind it is that I want to -- we want to -- build things on top of this. So it doesn't provide any big features. It's just minimal layer. We can add more capabilities on top of it. At that point, it doesn't have to be a framework. It can be a library. So now we have this minimal layer. We can create many libraries to add features, one of which is router. So until now, we didn't see any router.
JASON: Right.
DAISHI: That's why -- I think it's a unique aspect of Waku, which is router agnostic architecture. So let's now see how this thing works. Let's try our best. I can send a link, but can you open the browser and go to Waku GitHub repository.
JASON: Oh, yeah.
DAISHI: You can do that from there.
JASON: Let me drop a link in here for anyone who wants to see it.
DAISHI: And we have docs folder. And there's create pages.mdx. The create pages is the low-level routing implementation. So now, this is a library. This is not tied to the framework. It sits on top of it. So now can you copy this entry and paste it where we have now.
JASON: Full replace?
DAISHI: Yes.
JASON: Okay. So this is a full replacement. It looks like I've got some templates I need to copy.
DAISHI: Yeah, let's remove it. Okay. Go back to the doc. Now there's also client entry, which I added recently. Maybe down below. So we need to replace main.tsx. I think it's at the bottom.
JASON: Ah, okay. So here's our main.tsx. All right.
DAISHI: So the difference is that now we have router. It's Waku router client.
JASON: Okay.
DAISHI: And they own the server. It's Waku server.
JASON: Sorry. The one piece we were missing is in entries. We don't have these two files here. I was looking for where those come from.
DAISHI: You can comment them out.
JASON: Okay.
DAISHI: And we can directly say -- yeah, at line 12, make a row function with empty parameter and returns JSX, starts with HTML.
JASON: Okay. I'll do something simple.
DAISHI: Can you put body too?
JASON: Oh, yeah.
DAISHI: Just to make sure. Okay. I have never tried this. So bear with us.
JASON: Okay. Do I need to put a head tag in and leave it empty?
DAISHI: Yeah, empty head.
JASON: Okay.
DAISHI: And down below we can do the same, at line 28.
JASON: Oops. That was not the button I meant to hit. Like this?
DAISHI: Yeah, we don't need HTML. Just say H1. I hope it works, but I've never tried it in this way.
JASON: Okay. This is say it reloaded. We didn't get any crashes. So let's go out here and see what happens.
DAISHI: Oops, oops, oops.
JASON: Well, in good news, it is rendering our root.
DAISHI: We forgot the children. So at line number 12, we need to get children.
JASON: Okay. So here's our children. Then we'll set children here. And there it is. Just like we would expect. So, it works.
JASON: Beautiful. I'm following. This is great. So we've got our paths. We've got the component we're actually rendering. And I'm assuming this is like a render mode.
DAISHI: Exactly. So what it does is it takes these as input and returns what we call render entries function. So it translates this input to the minimal API we had learned just before.
JASON: Okay. Yeah. And if I wanted to add a second page, like let's say I want to add a second page here and we're going to call this one about. It looks like, if I'm understanding correctly, do I need to update this as well?
DAISHI: No, this is an array. So it should work.
JASON: And there it is. So we get our -- let me refresh that. Now we get what we would expect. That's very cool. Okay. So this is a super simple router. A lot of times all I want is something that just does the bare minimum so I don't have to configure a bunch of stuff. I love that this is that option.
DAISHI: Yeah. And again, the important part is this is built on top of the previous API.
JASON: Got it.
DAISHI: Okay. Maybe it's time to move on to the -- okay. Actually, I think this create page is router. This is production level. I mean, this is something for real. But we have many feedback about people saying they want file-based router.
JASON: Okay.
DAISHI: So we created a file-based router on top of this API.
JASON: Okay.
DAISHI: We can go from here. Maybe we can try. Do we have time?
JASON: So, one thing that I have questions about is like the idea of actions in React server components. I want to make sure we have time to approach that and try those out. That's something that Waku has, right?
DAISHI: Yes.
JASON: Do we have time to do both?
DAISHI: Let's see. Yes, we do, but they're two separate topics. No, actually, it can be done in one app, but I do not have a good demo to show things at the same time. So we should do twice. But I think we'll have time.
JASON: Great.
DAISHI: Let me continue a bit. Now there is -- I need to -- let's do something. Let's do some hack. Go to GitHub and go into the source. There's Vite plug-in RSC managed. This is something we'll see in a minute, but what I want is to cop if I from line 15 to 22 and paste it in our entries.tsx.
JASON: Umm... like here? No, that's incorrect.
DAISHI: Replace everything.
JASON: Got it.
DAISHI: Just remove --
JASON: It did something weird.
DAISHI: No, it's fine. Let's replace extensions. So because this is a template. Okay, I get it. I got it. Let's replace -- so, maybe I can control.
JASON: Yeah, go ahead. If you click in, it'll let you take over.
DAISHI: No, no, no. This is not the one. Can I type it? No, I cannot type it. I need to enable keyboard.
JASON: Oh.
DAISHI: Okay, okay. I can.
JASON: Ah, okay.
DAISHI: It's still complaining.
JASON: What are you mad about? It doesn't like -- it looks like valid code. And it's --
DAISHI: I think it's fine. It's just TypeScript.
JASON: Yeah, it's just complaining about something in TypeScript that I don't think is real.
DAISHI: So now if it works correctly, this is FS router, which is built on top of create pages API that we've seen before. We now need to create files.
JASON: So we could do like an index.tsx.
DAISHI: We can also have underscore layout.tsx. We should have deleted, but we can do it again. Export default function.
JASON: Oh, right. Because it's a -- export default function. And then that --
DAISHI: HTML blah, blah, blah.
JASON: Yeah, we'll get rid of this.
DAISHI: We need to take children.
JASON: Yeah, so we'll put children in here. And put children in here. So that's our layout. This will just kind of throw the whole thing in there. Then in our index, same thing. Export default function.
DAISHI: Exactly.
JASON: Okay. And this one is going to return -- we'll just do an H2 that says home. Okay. This I don't care about. We'll get rid of that entirely. And you're just a TypeScript error I'm not worried about right now. So let's make sure that's working as expected. We head back out here. We reload the page, and we've got home. So then if I change this, we've still got our hot reloading, and it's loading exactly as we would expect. Let's add another one. And I'll copy this and it's beautiful. So yeah, this is great.
DAISHI: One thing we can try is there's a link component that you can use. Let's try that quickly.
JASON: And do I want to put this in my layout or individual pages?
DAISHI: Either is fine, but layout will be good.
JASON: Yeah, let's do -- okay. So we'll put together -- we'll do a header in here. We'll do a nav. Is it a link component like this? Yep, it auto reimports for me. Great. And we'll say to home. And we'll copy that, change it to about. Oh, I screwed something up.
DAISHI: It's not working.
JASON: Did I do something wrong?
DAISHI: I'm not sure. Let me double check. It looks good, actually.
JASON: Okay. Let me just stop and restart it because we've been doing a bunch in here. I'm going to reload it. Oh there, we go. I think I just needed to reload the page.
DAISHI: Layout is static. So maybe we cannot --
JASON: So when I added the link, it added the markup but didn't reload to get the script.
DAISHI: Actually, I'm not sure what's happening. I'll look into it later.
JASON: Got it. Okay. Cool. But here we go. We're running. Now we've got, you know, the kind of standard experience I would expect.
DAISHI: Right. This is what people expect.
JASON: Yeah. This is great. Exactly what I would hope for.
DAISHI: And this is built step by step. It's built on top of the previous API, which is something I wanted. So people can do something similar with their own solution.
JASON: And this is really -- you know, what I like about this, too, is I think file-based routing to me, it's such a convenience thing. I really do love it. But it is very magical, and what I like about this is that you're explicitly saying, like, just glob this folder and turn those things into pages. And I think that's a really nice -- I think that's just a nice way to do it.
DAISHI: Yeah, this was pretty tricky. But it works now. All right. We have only 30 minutes left. I think we move forward. Let's play with actions. So let's create a new project.
JASON: New project. Okay.
DAISHI: Because we don't have time to code everything. So I have a small example up.
JASON: Is it in the create Waku?
DAISHI: Yes, let's choose from the template again.
JASON: Ah, I did it wrong. Choose, not chose. Okay. So this is going to be Waku server actions
DAISHI: 23, I guess.
JASON: Perfect. So we're going to CD Waku server actions. I'm going to npm install. I'm going to get init. Then I'm going to open this. Okay. So drop this one down. In here now --
DAISHI: Yeah, this doesn't use router. It's just to experiment how our actions work.
JASON: Okay.
DAISHI: So you go up component. There's a bunch of ways to call actions. One is to import directly from funcs at line number four. If you go there, there are some functions, just experimental functions with a server directly at the top. So the easy one is greet.
JASON: Yeah, so we --
DAISHI: It returns something, yes.
JASON: Nice and easy. We just kind of make sure it does what we want.
DAISHI: Then it's an up component. We pass function to counter component.
JASON: Okay. So this one gets the greet. Then we start the transition and set it.
DAISHI: To count from the state. So let's run the up and see how it works.
JASON: Okay.
DAISHI: Unfortunately, this is not pretty. It's not a pretty demo.
JASON: What have I done wrong?
DAISHI: I remember this. I already fixed it, but it's not published yet. So let's remove something from app.tsx.
JASON: Okay.
DAISHI: I think I will release it next week. Remove balancer on line 21.
JASON: There we go. Okay.
DAISHI: It reads count from the state, and the response is shown in the bottom.
JASON: So I can update it to eight, click the button.
DAISHI: Maybe we should open the network tab and see how it's working.
JASON: So here is the network tab. Let me make this a little bit smaller. And I'm going to increment, which doesn't do anything. Then when I click the button, we get new text.
DAISHI: In the payload, it's a request. It's in URL params. The server action receives this an input argument, and they make a response.
JASON: And we can see that in here. Because that's the name that's being passed in. We have the greet here. Then when we call it, we're greeting with the current count. So the C equals and the count. Okay. So this is handled client side, which gets sent to the server side function, which then gets returned to our app, which is --
DAISHI: Let's add more weight in greet function.
JASON: Okay. Instead of await promise resolve, click await new promise and set time-out. I guess I can just throw it in. That's a muscle memory thing I just realized. Okay. So this one then should take two seconds. If I increment, click. Boom.
DAISHI: We see pending level there. That's how transition works. If you go back to the client.tsx, there is start transition.
JASON: Button client? Sorry, where?
DAISHI: Counter.tsx.
JASON: Sorry.
DAISHI: If you comment out start transition, line 18 and 20, now it's not in the transition and doesn't show pending. It just runs behind the scene.
JASON: Got it. So it still runs. It still takes time. It still updates the button. But we don't get that indication that something is happening.
DAISHI: Yes. Which is sometimes useful but not for this time.
JASON: Yeah, and in this case, the use transition, this isn't one I've used before. So this is pretty slick. This is a React API. They just give you a state, like if something is transitioning, you know because is pending will be set. And a function to wrap actions around so you know when they're done. So this one specifically, when we use set text in use state here, it's just waiting for whatever we run to complete before it would set the transition back to false. Very cool. Okay. This is great.
DAISHI: What I had -- we can actually import server functions in the client component file. So if you open textbox.tsx, we actually import greet from func2, which has -- so previously, we need to pass the function from server component. In app.tsx, we passed greet function.
JASON: Right.
DAISHI: Which is passing a server function from server component to client component.
JASON: Yeah, so we have our server functions that are marked as use server. Then in app.tsx, which is a server component, we import greet. We pass that to counter. We've got greet here. Counter is a client component, but we have access to greet because it's one of the props. But then what you're saying is in text box, which is a client component, we are importing greet straight up. And here's the use effect that shows us that it's a client component. Now s this a feature of Waku?
DAISHI: It's a requirement from React docs as a capability of server function. But the framework has to implement it.
JASON: Got it.
DAISHI: It's a Waku implementation, but the spec is from React.
JASON: So this is actually something that I think is a challenge. In here, right, we've got greet. Now, if I'm using my environment variables and it's some secret, how do we make sure this doesn't leak into this that's importing it? Is there something where that gets stripped as part of doing it, or is that something we need to remember as we're building these apps, to not -- because I guess my use case would be if I wanted this action to allow somebody to sign up to my newsletter. I'm going to need an API key for my newsletter provider to actually submit that new email. But if I call it from a client component, how do I make sure I don't accidentally leak that API key?
DAISHI: I would say this is still an open question in the community, in the React community. But the way it works is that if it has a server direct, it doesn't leak. The hard question is how do we make sure. React has a new API called -- what was it called? It detects it somehow. But I haven't played with it yet. So yeah, it's a tough question. But the way it -- if you know the way it works, if you have a use server directive, it doesn't go to the client.
JASON: Okay. And the way that we can verify this is we can add this, and we'll see that it's loading on the server. But if we come back out here, we go to the console, we don't see that even when we greet. We've got something else logging. We don't see it show up in the client.
DAISHI: One other challenge was to use closer variable. So go back to app.tsx. No, it's actually button server. So this is also the spec from React. But the tricky part is that there are some variables outside this function. There are two variables. One is counter at the module level, at line three. The other is now, which is inside the button server, which is a server component. So every time we re-render, it has new date. We can do some experiments. We can open up two types and go to the same page. It should have different now.
JASON: Okay. Let me -- ah, let's see. Split, and we'll go to local host 3000. I want to split, though. Tab, split, there we go. Okay. So now I have button.
DAISHI: Yeah, one and two is the same. And you have to see the console log on the server.
JASON: Oh, I got you. So let's make this one here, and we'll open up the server. So here is our button one, button two. Okay. So this one has a set time stamp. Button two has a different count. Button one, different time stamp in the different tab. Okay. So I see what you're saying. That works.
DAISHI: So that's another capability of using server action.
JASON: The closure stuff always melts my brain. I remember back in the day when you had to do things like -- you had to do like this.binder or whatever it was. I can't even remember the syntax anymore, but you always had to kind of grab data and make sure that it was connected to the right context. Now we just get to throw it in here, and it just works. And that is very pleasant for me as a developer. I imagine that was very challenging for the people building it, yeah. Okay. So this is great. It also -- so, this is in the React spec. Is this working in all RSC implementations? Yeah, .bind this. That's the one.
DAISHI: Yeah, I do not know. I haven't researched.
JASON: Yeah, I haven't either. I know that I've seen a lot of people talking about this. Next has it working?
DAISHI: Yeah, I'm sure.
JASON: All right. So we've got about 15 minutes left. Is there anything else you want to show?
DAISHI: Yeah, let's move on. I want to show the actual usage.
JASON: Yeah.
DAISHI: So now create a new project without choose option, which is something people would first see.
JASON: All right. So we're going to do this. Waku actual usage. Then we'll open this up. Close down this one. And here is an actual usage. We have some pages. We've got components.
DAISHI: We have styles.
JASON: Okay. I see we've got some Tailwind in here. Otherwise pretty light, though.
DAISHI: Yeah, let's run it. Let me open up my terminal here, and we'll npm run dev.
JASON: So here is the actual project.
DAISHI: It's pretty much what you have done before.
JASON: It's looking pretty familiar from the standpoint of what I would expect from a framework.
DAISHI: Yeah, we have async get data. So it's just JSON here, but we can call data received if you want.
JASON: Got it. And this runs server side, correct?
DAISHI: Mm-hmm.
JASON: So this is similar to get initial props or the loader function in Remix or front matter in Astro. If I've got data, if I want to make sure I have the data available, we just do that, and then we grab it right there. What is nice about this is that it's not abstracted away, which is kind of interesting. Again, the cool thing about frameworks is they just do stuff for it. The downside is they hide how they do that stuff. So it is kind of interesting to see you opted for, you know, function get data and just called get data. We don't even have the abstraction of export a loader and call use loader data, which is a pretty light abstraction. But it feels -- I don't know. It feels nice. I feel like I know exactly what I'm getting.
DAISHI: Cool. Yeah, one last thing I want to add is there's a render mode. Render static. When you say render static, it's statically generated. We can actually run without server. I mean, we can just upload the file in a hosting service and it runs. So this is actually my primary use case. So if you have -- if all components are static, we can make static sites.
JASON: And if you're working with data that doesn't change very often, you know, it's really fast to build a static site. So using whatever hosting provider you prefer, uploading a static identity is going to cost you very little. The maintenance overhead is very low. The fragility is very low. So I'm very much with you. Go static wherever you can. It's going to save you some heartburn.
DAISHI: We can actually try quickly, if you open the terminal and say npm run build. It builds everything. Public folder is client side files. If you open up the RSC folder in public, this is exactly the one we've seen.
JASON: Oh, yeah. Okay.
DAISHI: In the network tab. So instead of running our Waku server, we can go into the terminal and type npx serve dist/public.
JASON: Okay. So now we should be at local host 3000.
DAISHI: Now this is not running Waku server. It's just static file.
JASON: And if we maximize this, reload the page, we can see that we've got --
DAISHI: Maybe go to the about page, and you'll see the about text.
JASON: And there's that about.txt.
DAISHI: Which is static file.
JASON: So this would be really nice. Just a very easy thing to deploy. We can even do things like if we go to app.netlify, we can go into this GitHub folder and get -- Waku is going to be down at the bottom here. Actual usage dist and public. Then I can come down to the bottom where my deploy a new site thing is and literally drop this, and this will be a live site. So now we've got a production deploy that I can drop in here, and y'all can go and actually play with a Waku site in production. This is one of the reasons I love static deployment so much, by the way.
DAISHI: Yeah, yeah. I totally agree. By the way, we have -- we support Netlify, both for the serverless function and static site. Not sure where it's described.
JASON: So here's the Netlify setup. Here's the Vercel setup.
DAISHI: It's exactly what you did.
JASON: Really, really nice.
DAISHI: I think I covered everything I wanted.
JASON: This is cool. And the nice thing about it, too, is I feel like for a lot of us, myself included, the introduction of React server components, it made me feel like too many things had changed in the React ecosystem, and I was worried about what that meant for building with React going forward. To see the way that Waku is set up, it's sort of stripping back and saying, look, if you can deploy this minimal thing and just see how a React server component works, then you can see this instance of a functional site. When we look at the code for this, this is not an intimidating app to use. It looks very similar to things I've built before, and knowing I'm getting the advantages -- I imagine the React team wouldn't launch React server components unless they thought it was going to make a difference, right. So knowing that I'm getting the advantages of that without having to relearn the way that I write react, I think, is really nice. I have a question from the chat. Would it be possible to embed Waku into other frameworks like Astro or straight up express?
DAISHI: Not sure about Astro, but basically, it's a standalone framework. Embedding might be hard. However, it exposes some low-level API, and you can embed it in other server framework. Actually, it uses Hono underneath. Technically, it's possible to put Waku-built things in Hono. But I haven't tried.
JASON: Sure. And I mean, you know, I guess it sort of depends on what your use case is as to whether or not it makes sense to try to put a framework inside of a framework that, in my experience, at least, often leads to pain. (Laughter). But so, Daishi, this was amazing. Thank you so much for taking some time. Are there any other places that I should send people for additional documentation? So we've got the Waku homepage. We've got the Waku repo. I'm going to send another link to your Twitter. Is there anywhere else people should go, any other resources?
DAISHI: I don't think so. This is everything.
JASON: Okay. Perfect. Well, with that being said, then I think I'm going to call it there because I don't think we have time to -- unless there's something quick you wanted to cover that we didn't get to.
DAISHI: No, I think I have covered everything.
JASON: I think this is great. So, thank you very much, and to everybody watching, thank you for hanging out. Make sure that you go and check the schedule. You can like this stream, share it with your friends, get on the Learn With Jason newsletter to make sure that you get updates about more things like this. The easiest way to get notified when we go live is actually the Discord. I saw Fuzzy in there talking about the Twitch updates not happening that fast. So go check the Discord. We got good stuff coming up. A lot more than what is actually listed here. We're actually booked like until the end of October. I just have to get things on the site. So, coming soon on that. And one more thank you to our sponsors. We've had Rachel here from White Coat Captioning. That's sponsored by Netlify. Thank you for that. You might have noticed that Daishi and I were using Tuple to share cursors, and he took over the screen a little bit. It's a nice tool for pair programming. Make sure you give Daishi a follow. And I think that's everything. So, Daishi, thank you one more time.
DAISHI: Yeah, thank you.
JASON: Thank you so much for watching. We will see you next time.
DAISHI: Bye.