Let's Learn Vue 3!
VueJS has a great reputation, both as a community and as a framework. And in Vue 3, there’s even more to love! Ben Hong will teach us how we can get started building with Vue 3.
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: Hello, everyone. Welcome to another episode of Learn With Jason. Thank you, Ximena. Good timing, by the way. Today on the show, we have the one and only Ben Hong. Thank you so much for joining us. Thrilled to have you here. For those not aware, Ben just joined the team I'm on at Netlify. So we've been co-workers for the last couple months. It's been a pure delight. Ben is one of the nicest people I know. But for those of us who don't have the pleasure of working with you, you want to give us a background?
BEN: Yeah, sounds great. Hi, everyone. As Jason mentioned, I work on the DX team at Netlify. You might know me from some of my other work in open source. I'm on the Vue JS core team. As far as a little about me, I don't have a computer science background. I have a psychology -- I studied psychology in college and kind of took a real roundabout way to get into tech. This was prior to boot camps and stuff. It's been a real interesting journey. Sort of progressing along that, being self-taught and all. So, glad to be here.
JASON: Yeah, and I'm really excited for what we're doing today because we're stepping outside of my comfort zone. I don't do a lot of Vue programming. Today we're going to learn Vue. So I've seen -- you know, we've had Maya on the show to teach Vue. We've done some work with Nuxt. But I still -- I feel like whenever I look at Vue, I feel like I almost get it. And so today I'm really excited because we're diving right into just the beginner-level stuff. We're going to walk through, this is what Vue is, this is why you should use it. I think this is going to be really, really useful for me especially, and I hope for everybody watching this is going to be really useful for you. Maybe we should start first with just a general philosophical question, which is, why would I use Vue?
JASON: No kidding.
BEN: Yeah, so for me, that partly speaks to the approachability that Vue provides up front.
BEN: Yeah, so the way I like to think of it is that Vue sort of takes what works really well about React as far as being able to get in the weeds. So one of the things that we'll get into later, you can use JSX and render functions in Vue. At the same time, with single-file components, you have that initial approachability of just having that HTML. So directives are one example of abstracting away something that's really common, such as looping through elements and making it easy for people to do that within their markup.
JASON: And a directive is when I've looked at sample code, it's like the V-4. That's a directive?
BEN: Yep, uh-huh. So basically, sprinkling a little bit of magic HTML attributes to super power what you have and do some pretty awesome things with it. Taking care of those usual scenarios like hiding things and those sort of things. We'll be talking through some of that later.
JASON: Cool. Very cool. Actually, I think that might be as good a point as any to switch over and start taking a look at this stuff. So, the first thing that we're going to do is we're going to go follow Ben on Twitter. Here's Bencodezen. Also, this show is being live captioned. It's available at lwj.dev/live. That live captioning is provided by White Coat Captioning, and we've got Rachel today helping us out. So thank you so much, Rachel. The captioning is made possible through the sponsorship of Netlify -- oh. My sponsors are out of date. It's Netlify, Fauna, Hasura, and Auth0. I apologize for getting that out of date. We've got a new sponsor, which I am so excited about. I love Hasura. It's one of my favorite tools. We're going to do a little today. So you know, stay tuned for that. Thank you to that. They help make the show more accessible to more people, which I am just thrilled about. So, with that being said, you're now, I assume, dear chat, following Ben on Twitter. You've checked out the live captioning. You've checked out the sponsors. Now we're ready to learn some Vue. So I'm on the Vue website. What should I do first?
BEN: Well, right now you're on the Vue 2 website. You want to click on the banner up top because we're going to dive into Vue 3 today. The first question people ask is, oh, so is the stuff on Vue 2 out of date? Vue 2 keeps most of the concepts from Vue 2 basically, but it makes it smaller and more performance. So you don't lose out on the models you're used to in Vue 2. And you get new features on top of it.
JASON: Excellent, okay. So the first thing I want to do then, my instinct is to click this get started button.
BEN: That's right. Go ahead and click on that.
JASON: All right. This doesn't help me. What is that?
BEN: It's supposed to help you with the pronunciation, but it doesn't help me either. That's why we have the "like view".
JASON: I do love pronunciation guides with actual -- like, what is it, phonetic spelling? There's an actual name for this I can't remember. Sometimes I get it. Like, I know the sound the upside down E makes. I do not know what this sound is. (Laughter) Okay. So, installation. Normally, what I would do is I would read through this, but today I'm going to let you be my getting started guide. So what's the first thing that I should do?
BEN: All right. So, for those watching, typically -- so the docs we spend a lot of time on, so as you have questions, it's basically a course written out for you. If you find anything questionable, we have architected it in a way that really gets you the essentials to get started. A lot of people, even if you give them 30 minutes with the doc, it gets them up and running. So this alone is a huge resource for you to keep in mind. As far as installation, one of the first things -- so there are two primary ways people use Vue. One, especially in the docs, we use the CDN. We just drop a script tag to index HTML. We use that as a way of making it approachable to people who may not be used to building tools. But by no means is this typical. So for those wondering why we have a CDN, it's primarily for when you're doing legacy migrations, that you're not ready to necessarily move everything to a build tool. Then CDN is a great way to slowly enhance it and sprinkle and slowly migrate towards that.
JASON: That makes sense.
BEN: Yeah, and so today we're going to use the Vue CLI to scaffold our app. We have the docs open in the next tab over.
JASON: So this is cli.vuejs.org. So I'm going to drop that. Oh, wait. Phil is here to troll us. What is this, Phil?
BEN: Phonetic spellings are often very useful in dictionary definitions. (Laughter)
JASON: Well-played, Phil. Well-played. I actually am just now realizing that you spent the time to get this out -- wait, did you just get this out of a dictionary or take the time to phonetically translate this? I'm too upset to wait. So we've got the Vue CLI docs. All right. So there's another get started button. Let's do that. I'm going to get started. Documentation is for Vue CLI.
BEN: Yeah, a high-level overview. So the main thing most people would want to get to is the installation. That's probably what most people are interested in.
JASON: Okay. So let me do this. Boom. All right. We're installing the Vue CLI. And this should happen, hopefully, pretty quickly. We'll see. And while we're waiting for that, I'm actually going to -- we put together a little bit of like prep work on this. Or I should say you put together prep work after we talked about what would be a fun thing to do. So what we have here that I am very excited about is a small dictionary of nostalgic toys, things that when I was a wee one, I wished that I had these. So, this is the Teenage Mutant Ninja Turtles pizza thrower, I always wanted one of these. X-Men Cyclops action figure. Oh, Beast Wars and Transformers?
BEN: Got to represent both sides.
JASON: Getting spoiled over here. Bop It Extreme. Yeah, I never won that game. A Tamagotchi. I have a graveyard of Tamagotchis in my past. A Super Soaker, Pokemon cards, Magic Mitt. That was like a dinner plate with Velcro on it, right?
BEN: Yes, that is a great way of describing it. Dinner plate with Velcro on it.
JASON: Then a Nerf Blaster, which I was never allowed to have because my mom wisely knew that as soon as I had one of those, I would lose every single dart.
BEN: Yep, that sounds about right.
JASON: Okay. So this is going to work, I think. This will be good. So what we're going to do -- then we have images for each of these. These images are right here. Cool. Then, I think, hopefully at this point we're installed, which means Vue is installed. If I get the version, we're at 4.5.7.
BEN: That's fine. That's good.
JASON: I think I might have this globally installed somewhere else as well and have issues. We'll live with it. Okay. So I'm in this directory. I've got our data JSON with the toy names, the images. Just a read me. What should I do next?
BEN: Yeah, so I'm going to show you two ways of getting set up. Like all good CLI tools, I'm going to show you the default way, which is if you do Vue create with a dot, that'll create the application in the current directory. Then hit enter.
JASON: Is this going to explode because I already have a Git directory?
BEN: No, it should be fine. It should know how to respect that.
JASON: I guess I would like to generate in the current directory.
BEN: So let's do the manually select features. That's something worth showing people. So when you hit enter, you see that you can scroll down and basically choose the various options you want to configure. If you hit spacebar, that will toggle the different things. So TypeScript, Progressive. So let's turn on processors.
JASON: Thank you for the sub, Andy.
BEN: Then if you hit enter --
JASON: We don't need anything else, right?
BEN: No, not for now. But I'm going to show you the other way. We would choose like 3X for example.
JASON: Do you want me to keep doing this or quit?
BEN: So this, I think, shows you what you can configure. The other thing worth noting is you can save the preset. Once you have manually configured it, you can say this is my JSON preset. So if you hit enter on that, choose any one of those. Then you can choose your ESLint settings, whichever one you prefer. And you can either lint on save or lint on fix and commit. That will help with that. Either package JSON or dedicate conflict files.
JASON: Then you can save it as a preset. I want to quit this, right?
BEN: Yep, so control, C. We're going to talk about a feature that most people don't know about Vue CLI. I want you to type Vue ui. Then hit enter.
JASON: Oh, wrong browser. One moment. Over here. There we go.
BEN: So what we're going to do is create a new project. Yep, click right there. So we're already in the Vue 3 directory. This is actually a full browser -- or sorry, file explorer you can do. Go to create new project button at the bottom.
BEN: Then let's go ahead and I think you can leave it blank, actually. Actually, it looks like it might be forcing it. That's fine. So choose whichever one you prefer. Looks like we need to add -- or let's just type app for now. I don't know why it's doing that, but it's fine. We can move everything over manually. Just type app and we'll move everything in a bit. Then you hit next. So let's go ahead and this time we can do the preset. Actually, I want you to scroll down. I want you to add a pre-processor. I'm going to show people that in a little bit.
JASON: Okay. So manual.
BEN: Yep, manual. Then hit next. We're good there. Let's add the CSS pre-processor. Great. Then that's all we need for now for this one. Then let's use node sass like we did before.
JASON: This is extremely cool.
BEN: Oh, it's just the beginning.
JASON: I'm not going to save this.
BEN: Yeah, don't worry about saving it. So this will scaffold everything.
JASON: I'm kind of blown away. Oh, we're level two on the hype train. Choo-choo! I still don't 100% get the hype train, but I know I get excited every time it happens. So it's working. Oh, and in the background it's doing stuff. Look at that.
BEN: It's actually setting stuff up, yeah.
JASON: So this is really -- what's really exciting about this is if I'm understanding this correctly, this isn't actually doing anything eastern running CLI commands for us, but it presents a UI so that we don't have to -- like, so that we're able to kind of visually manage this, is that right?
BEN: That's just the beginning, Jason. Oh, that's just the beginning. You see we're inside the project dashboard. I don't know about you all, but I never remember the command to kill ports. Sometimes you just have things that are just running. So that's conveniently there for you. In case you have something on 3,000 or whatever you can't seem to kill --
JASON: Look, you can click this button. We'll be there in an hour. (Laughter)
BEN: So, okay. Let's go ahead and on the left-hand side click on the plug-ins icon. There you go. So it actually shows you all the different dependencies you have installed, and you can manage them all from here. You can see you can go straight to the docs. It tells you what the latest version is. Basically, you can manage your plug-ins in a visual way and get meta information you may not get in another way.
JASON: This is outstanding.
BEN: Then let's get to the next icon. I think, if I have the right one in my mind. Oh, that's dependencies. That's fine. That we can deal with later. Let's get to the gear icon. So in here, if you click on the ESLint configuration, you can -- so, I don't know about you all, but go into the rules in the tab on the top.
BEN: You can configure all your ESLint rules without memorizing is it two, is it error, error warning? Which one do I want?
JASON: Like, having to memorize those has actually led me to not -- I just use ESLint as a way to run prettier. That's where I've landed on ESLint. (Laughter)
BEN: Yeah, so this allows you to do all your configurations from here in a way that doesn't have you memorizing syntax and looking at docs to do things, such as whether you want indents, semicolon, whatever.
JASON: That is absolutely fascinating. Just, well done. It's just really good work. Really solid all the way around.
BEN: Yeah, we love the Vue UI. It's unfortunate more people don't know about it. So let's actually start off our local web dev server. This is the final touch I'd like to add. This is all the way down to the clipboard.
JASON: Task, got it.
BEN: And we're going to run the serve command. We're going to click the run task.
JASON: Okay. I can see things happening down here. It's building. It's idle. What?
BEN: All right. Then if you scroll down, you'll see that we have some basic speed stats based on your build. Again, this is sort of standard algorithm stuff. Then you see all the different assets you have and how they relate to one another as far as what's taking up the most space. So if you click on the analyzer next to the dashboard --
JASON: What is happening?
BEN: You'll see there's even a visualizer for you in case you want to be like, what's taking up so much space? You can click into it and be like, oh, I imported the whole lodash. I only wanted one part. So this kind of stuff is here for you, and this is just the stuff they had come up with Vue 2. So I look forward to what the team does for more Vue 3 enhancements going forward. But this is all available for everyone already. You can use it with your Vue apps already.
JASON: I'm just kind of like low-key mad at everyone for not telling me about this earlier.
BEN: But here we go. We have our Vue app. It is now up and running. I think we can now officially dive into some of the Vue stuff. But yeah, that's your Vue CLI.
JASON: That is legit -- like, I'm taken aback. I feel like there's this space between the tools that exist and the people who use them. There's a level of thought and care that typically gets skipped. We're like, it works, it's good enough, let's go. This feels like someone said it works, but it could be better and took that extra level of thought and care to go and build an experience that is like actually embracing the idea that somebody might be using these tools for the first time. That's one thing that I think is kind of absent in a lot of the JS ecosystem. You'll go out there, and every project is using web pack and Babel and ESLint, and they just kind of assume you're going to go to the web pack docs to read how that works. And this is really outstanding. Just well done to the whole team.
BEN: Yeah, it's one of the things I try to -- one of the things that also makes Vue great is because it's an open-source project with no corporate backing, it is designed by the community for the community. So what a lot of people don't know is the core team is basically made up of volunteers. I think there may be one or two that's full-time, but most of us are just volunteers who love the work that we do and just want to help make the ecosystem better. So it ends up with some different priorities when it comes to building features out.
JASON: Absolutely. That's -- I mean, okay. You've won my heart. Now let's go win my mind.
BEN: All right, sounds good. Let's do this. So let's open up VS code. Rather than -- let's just have you drop -- let's have you move the images into the app for now. I think that'll be the easier thing. So if we move the images into public, I think, would be the easiest place. So Vue by default will serve everything in public as like a static path. Basically it's a relative path. So we'll use that to call things later on. I think with that, we are good to go from a getting started perspective.
JASON: Yeah, so then we've got this here in our HelloWorld. It pulls in our HelloWorld component, so I can look at that. I'm just going to change out -- oh, I can change the message here. Let's change this message to instead say, "hi, chat." And this is running, so it's already updated. I'm going to pull these over here so that we can work on this. What a nice little getting started flow. So, what should we do next?
JASON: And this is the sort of thing, like if we go in here and create a paragraph tag and then we create a button, then in our styles down here, I can just style this paragraph tag and we'll say, you know, color red or something. But then I can nest inside of this and do something like this. It's going to be really good looking. I forgot to add actual text in our paragraph. So we get red text and a blue background and our button. It's a highly inaccessible garbage piece of code, but we've immediately got set up with the sass nesting. So any buttons outside of a paragraph wouldn't get styled, which is really nice. So I'll clean that up. Now, you know, we're about 30 minutes in. Our goal today is to turn this from the default Vue app into a nostalgic wish list. I guess maybe the first thing we want to do is just kind of build our own component and not use the HelloWorld.
BEN: Yep, definitely.
JASON: Okay. So, I'm going to do that. Let's call -- naming conventions don't really matter here, right?
BEN: Nope. Typically, we recommend avoiding one word so you don't conflict with the HTML spec. Otherwise, it's free reign.
JASON: What about capitalization?
BEN: So you see that HelloWorld there, it automatically does it. It'll identify it correctly. So your team can once again choose what makes the most sense.
JASON: So I'm a kabob case kind of person. So I'm going to go with that. From here, if I look in here, I can just do -- I can just start with a template, right?
BEN: Yep, that's the beautiful thing about Vue single-file components. You can just include the thing that matters to you.
JASON: Yes. And then, h1. So I've got an h1. Then I'm going to come out here. I'm going to get the wish list. Then we'll swap this out, and I don't need a message anymore, so I guess I can drop that for now. Then -- hey, look at it go.
JASON: So I'm going to clean up this logo as well.
BEN: Yep, go for it.
JASON: All right. And I'm going to delete this HelloWorld because we don't need it.
BEN: Please do.
JASON: Okay. We're done, right?
BEN: Yeah, we're done. That's it. We can close up shop. (Laughter)
JASON: Okay. So the first thing that I would reach for if I was at this point in the app is I've got that JSON file with all the information. So my first instinct would be to import that. So in Vue, do I just -- I can just import it? Is there anything magic I need to do?
BEN: Well, so the only thing is you would want to open up your script block since the Vue file is doing parsing. It basically only knows what language to use once you've incapsulated it. So we can import our data. You can name it whatever you want.
JASON: And do I go into public or root?
BEN: Yeah, so you do the relative path. You can to . ./ I believe. There is an alias with the at symbol. I like to do the relative because it's more explicit.
JASON: That makes sense to me. I was wondering if it was going to force me to do something because this public directory gets handled differently. I wasn't sure if it was going to have me do something different.
BEN: At this point, you should be good. It's when we want to call it from the template, like the image, for example. It'll literally follow that path. Cool. So now that we're at this point, we need to learn about the data store for Vue. Inside of our objects, Vue allows you to define these options that basically handle scenarios for you. So the option we want right now is data. So we type data. It's going to be a function. So we need it to be a function that returns an object. In this regard, it's similar to React in that we want to make sure it's being kept -- yep. So if you just wrap that in parentheses. Perfect.
JASON: Like that?
BEN: Yep. And just save it like that. So what this means is now Vue is keeping track of this gifts data property. Basically, if any changes happen, any dependencies will update accordingly.
JASON: Cool, okay.
BEN: So if you want to render that on the screen, let's open up a P tag on our h1. Oh, sorry, a paragraph tag, not the pre. Actually, either way works. Then just save it.
JASON: Hey, there's our list.
BEN: There's our list.
JASON: Very cool. Okay. So, that's exciting. What are you yelling at me about? The template root requires exactly one element.
BEN: So I think that's an old ESLint thing. Right now you're in Vue 3, so I think that's not pulling in the latest one for whatever reason.
JASON: But we can fix that. I can go here.
BEN: You could.
JASON: And I can go to my rules. Let me make this bigger so I can see what's going on.
BEN: We search for multiline, I think. That's what we're looking for. Oh, sorry. Root element. The rule is probably root. Yep. Disallow -- yep. Then it should be off. Save changes, boom.
JASON: I wonder if it's --
BEN: Might need to restart the server.
JASON: Oh, wait. ESLint -- yes. Allow? It could very much just be my -- it's probably my setup causing problems. Let me -- ESLint -- oh, no. Why don't you just stop? This is almost assuredly my ESLint VS code config, not the CLI. It probably pulled in some default a long time ago, and I don't know where it is or why.
BEN: There probably is an extension in there somewhere. But to answer Linda's question, yeah, so one of the new things about Vue 3 is you no longer are required to have that single root element. You're allowed to have multiroot components, which is pretty exciting.
JASON: Yeah, I'm not sure how to do it. We'll just ignore it and everything will be fine.
BEN: Everything will be fine.
JASON: So, back to our files. We have our wish list. Ignoring you. Now we've got a list of gifts. So then I would want to loop through these.
BEN: That, you would.
BEN: That is not how this works. So you want your HTML to be structured. It goes content first. Would you want to do an unordered list?
JASON: Yeah, let's do an unordered list. We'll mock it up here. We'd have a list item, and in each list item, we would want -- what's in our data?
BEN: So there's the label, the image, and the vote count.
JASON: Okay. So let's go with an h2. That would be our label. Then in here, we would want an image tag. That would be our image. Alt, we can reuse the label. Then we would want a current count. So I guess, just like a number tag.
BEN: Yeah, a paragraph tag. I think that'll be good.
JASON: Ideally, we want a button to wish for this.
BEN: Right. Great. So we save that, we should see that on the page.
JASON: So these are all broken because I didn't use actual image tags or anything. But the mark-up is there.
JASON: Okay. Then once I have that, I should be able to just do this, right?
BEN: Yep, gift.label.
JASON: Then does Vue want me to keep these quoted or unquote them? How does that work?
JASON: So this thing?
BEN: Yep, mm-hmm.
BEN: Then your alt.
JASON: Okay. I'm with you. But this one, we don't need to bind it. We can just gift.votes. Oh, I broke something. What I did break?
BEN: Oh, don't worry. That's the key. We'll take care of that in a second. So right now, here's the thing. It's just like React. Because it's rendering the different elements, you want to assign it some sort of unique key that will allow Vue to do its optimization. So to the right of the V-4 loop, it's just key. You want to V-bind:key. I already included an ID in there. So you can gift.id. Well, there we go. We're already starting to see things, so this is good.
JASON: So just to get this to stop yelling at me, I'm going to wrap the whole thing in a div.
BEN: Sounds good.
JASON: Because I don't want it to look like this is broken. So, it looks like we have a small problem, which is that these image URLs, I think, are not correct. Yeah, we need to prefix what that image is.
BEN: So let's go ahead and go back. One thing I want to make note of. All the V-bind we see in here, this is pretty verbose. So we can delete the V-bind from all of them and use a colon. That's the shortcut to let Vue and other people know that now it's dynamically bound.
JASON: Oh, okay. All right. I'm into that.
JASON: Oh, okay. So, like this kind of thing.
BEN: Yes, there you go. Yep.
JASON: Oh, wait, images.
JASON: Yeah, look at it go.
JASON: Oh, the waves of nostalgia washing over me. This is delightful. Look at that thing. Wait, this is a different Bop It. Oh, it's Bop It Extreme. Oh, okay. All right, all right. Good, okay. So we need to style this up a little bit. Are these scoped? How does CSS work?
BEN: I was just about to get to that. You're reading my mind, Jason. So let's give this a class. What do we want to call it? You want to call it gift grid or something?
JASON: Just wish list or something. Then we'll style directly to these elements because we have sass. We can do that. So we'll go style and it was lang SCSS, to tell it to parse?
JASON: I don't know how to do sass. I only know CSS.
BEN: Yeah, that's my preferred processor too. So now we do dot wish list.
JASON: We'll make this display grid. Then we can go grid, template, columns.
BEN: 12 items.
JASON: Okay. So what we can do is auto -- no, what is it? Repeat, auto fit. We'll make them like 150 pixels. What does that do? Nope. Element is missing.
BEN: Oh, did we not close something off?
JASON: Oh, that's my bad. So I screwed that up real bad. Oh, 15 pixels. That would do it. Then I need --
BEN: So we need the image. Oh, yeah, grid.
JASON: Then down here we need our image width to be 100%. We're getting closer. Getting closer. Let's make our h2 much smaller. Whatever the default size is.
BEN: Maybe a grid column gap.
JASON: List style of none. We also want to reset the padding on that list item. So we'll go with a padding of zero. Good call on the gap. So we can set a gap of 1 rem. That's looking okay. Let's line these up in the middle. We'll justify content center. Beautiful.
BEN: Look at that.
JASON: Now, as we drag this in and out, what we should get, automatically responsive grid.
BEN: Nice! Beautiful. All right. So now I want to talk a little bit about how we manage CSS in Vue. It's something that a lot of people don't know. So right now, as you might imagine, since this is just a normal style, this will be brought into the global scope. So anything else with that wish list -- yeah, so we inspect the element. You'll see right now it's just a normal CSS class. This means it can bleed into everything else. So when it comes to component architecture, we want to try to scope our CSS to incapsulate what's going on. So what most people know, who develop in Vue, is if you add the scoped attribute to your style right now --
JASON: Just write scoped?
BEN: Yep, just add scope and save it. If you actually inspect -- actually, yep, there we go. You'll see that Vue has now added a data attribute to our HTML element.
JASON: Oh, and look at this.
BEN: Yeah, so now it automatically appends that to your CSS. So this is really good and what most people use. For those of us who have used CSS a lot, it still doesn't protect us from high specificity and side effects from commonly used names. The other one I've been trying to introduce people more to is CSS modules. So if you swap out the scoped to module -- we'll need to do one more thing, though. Then let's go up to the top inside of where we were using the -- yep, class. So we need to V-bind the class and pre-pend it with that colon. Then before the wish list, we need to access a global style object. So it'll be dollar symbol, style, dot wish list. For those of us who write CSS, this feels icky. But the payoff is worth it. So when we save it, you'll see something interesting is going to happen. Is it refreshed?
JASON: Yeah, it's here.
BEN: There it is, yes. So you'll see Vue has done something really interesting, which is it has pre-pended your CSS class with the component it comes from, along with a unique hash as part of the CSS class name. It basically made it possible for you to now truly scope your CSS in a way that basically it'll never, ever have conflicts.
JASON: So CSS modules are something I'm a huge fan of. My major reasoning for it is that I don't want a lot of rules in my CSS. I don't want to have to remember to use those kind of very verbose class names. And I don't want to have to memorize a bunch of utility classes like in tail wind or any of the approaches we've seen there. So from my standpoint, what I like about CSS modules is they give you the security of something like bem, where you know that the class is going to be unique. You know that all those things are happening. But you also get that flexibility of just being able to write CSS and not being -- you know, I don't mean that to say I don't like Tailwind, but personally, I would prefer to write CSS over trying to assemble tokens. That's the way my brain works. I've been writing CSS for a long time, so I get more frustrated by the tokens than by trying to remember what a CSS property is. So this is really exciting to me because when you get to write a module, I get to do it the way I want to. I call the class wish list. That makes sense. If I have 50 components and all 50 of them have a wrapper, I get to call that wrapper in every single one of them, and I never have to worry about conflicts because they're always scoped out like this. I just -- I'm a big fan of that.
BEN: Yeah, and I've seen CSS modules handled a number of different ways. I love the way Vue CLI handles it. By giving you something that's human readable and not just a hash, because that's one approach, just to make all your CSS classes hashes. When it comes to debugging production, you're out of luck. Whereas with this, you not only get the wish list you used but the component that it's coming from. So especially on an enterprise level application where you might have a billion things called dot button, that makes your job so much easier for figuring out where to start debugging things. That's what good tooling is about, helping us when things go wrong.
JASON: Yes, and the difference between a module and scope is that when we do it scoped, if I go back up here and I'll just undo this quickly. So notice how the module has this customized class name instead of using dot wish list. If I save it as scoped, it keeps the regular class name and then uses the data attribute. So this is good, but I could still break this if somewhere else in my app I was to do like wish list dot wish list. It would break my app. So it's not actually scoped to this module. I can go in and break this. So by using the module, when I save this, then I can go in and I can do something like dot wish list, dot wish list. Now my bad decisions elsewhere can't break this module. Hey, what's up, Chris? Thank you for coming by. I hope Advent of Code was fun today. Welcome, everyone. We are building a nostalgic holiday gift list in Vue today. So, thus far what we've covered is we got into the Vue CLI. We looked to the fact that the Vue CLI actually has a whole uI attached, which is incredible. So check this out. Right now we're running our serve command from a UI. It's running here. I can actually turn off the server and all this stuff. And we can see this whole breakdown of everything that's happening. Very cool. I assume this here is because we're using unbundled things.
BEN: I believe so, yeah.
JASON: So we'll fix that later when we actually build it.
BEN: Right, because it's all local right now.
JASON: We have our list here being pulled in from our data.json. There's a list of images here. We've rendered those out on the page using this wish list component by importing them, adding them to our data, and then we use Vue directives to loop over the gifts to display them on the page. Then we're styling them with scoped styles -- or CSS modules, in fact, which I'm very excited about. This is all very exciting.
BEN: Yeah, and so there's one other feature I'd actually like to show that I think would be helpful and sort of showing an important Vue concept that might not be as common in some of the other frameworks. What I want you to do is under your h1 add a text input. As developers, forms are something we deal with a lot. So we can imagine, what if we wanted to filter this wish list and basically see what the heck is going on. So, right, you want to be able to track that. What we're going to do is inside our data store inside our script, let's add a new property and call that filter text or user input. Whatever you want to call it. Then we just make that an empty string. Just leave it like that. So here we want to create -- we want it to be reactive so any time the user types into the text box, we want it to update the data store. This is fairly common. So in React, for example, you probably need to bind an input event or a key up and have that update your state accordingly. So since this is such a common occurrence, basically Vue helps you out here with the V-model directive. Inside the input that we have in our HTML, if we do V-model and then equals, and in this case we call it filter text. So basically, we're telling it what data we want it to update. What we're going to do here is under the input, let's just render out filter text so we can see it update live for everyone. Then if it's broken, we'll know it's not working. So this should be just an empty string right now. Then if we save that and start typing.
JASON: Do I need to reload?
BEN: Just hit refresh. There you go. And there you go. You now have reactive properties. So you're now tracking your text. But this is not the cool part. What we really want is to be able to filter our wish list based on what users are typing. So this is where computed properties are a really big thing in Vue and make a huge difference when it comes to managing data. So, if we scroll down into our script block, what we want to do is we want to actually -- so, next to data -- sorry, underneath data, we're going to define a computed option. So that's going to take an object --
BEN: Yes, computed property. So this will be an object which will contain all the properties we want to track. Let's call it filtered wish list. Let's be very explicit. And it will be a function because we're going to return something from it. So in this case, I don't use the arrow function because it's going to reset this context. This is the one thing about the options API that's a little more magical. So here -- so in this case, for example, what we want to be able to do is say return this filtered text for now. I need to demonstrate what's happening. Plus, then just add a string that's like, boop.
BEN: So this computed property, if you think of it as -- it's a reactive data property that then launches dependencies and will update and cache itself accordingly based on the calculations it does.
JASON: So to trace what's happening here, we define filter text as being an empty string. Then when we use V-model, we're saying, hey, this input should look at the value of filter text as an initial value, and if I change the input, it should update the value of filter text accordingly. Then computed says any time the data changes run this function and reset this value.
BEN: Yep, exactly.
JASON: Then can I just use this as like any -- like a normal value?
BEN: Yeah. So if we go to our HTML and add a P element and say filter -- what was it, filtered wish list.
JASON: I copy/pasted that to make sure.
BEN: Now if we type, you'll see that Vue has taken care of this piece now. It knows this is a dependency. As it updates, it will update everywhere else.
JASON: That's really nice. Okay.
BEN: So what we're going to do now is we're just going to use this. Sorry. So inside our filtered wish list, we're going to -- so, the this context is the one magical thing.
BEN: Yeah, Vue tries to give you -- especially with the options API. Jason knows what he's doing, so I'll talk while he's setting up the filter function. It tries to give you some conventions that help you with most of the use cases you often have to encounter. But then in the event you want to break out of it, there are escape patches, and you can define things manually. So in the instance of template, for those who want to programmatically generate the template, we have a render function for you as well. If you want JSX, there's a plug-in to integrate that in. So there are escape hatches in Vue to do things however you need to do it, suited to the problem. We just know that a lot of times, you know, most of the time you try and do this one thing, so rather than make you re-create that function every time or debate whether or not to use a four loop -- like, that's why we have V-4. It takes care of the performance for you and you just focus on looping through and solving the problems that matter to you.
JASON: I'm doing this wrong. So if the gift label contains this, then we're going to return -- wait, what am I doing? No, I was right. I was right the first time.
BEN: Yeah, I think you were right. Is it includes? I might be wrong.
JASON: Nope, you were right. Oh, I'm putting it out here. So instead of this, I should just have to say filtered wish list instead.
BEN: Yep. Now type. Do the things. You mistyped.
JASON: Wait, no, I didn't.
BEN: Capital F. What are we checking?
JASON: Oh, it's case sensitive. So I need to make it case insensitive. Okay.
BEN: Is it still adding the boop? We can make it add the boop, Cecilia.
JASON: Lower case, and then we'll take search equals this filter text to lower case. Then we'll say if label includes search. So now it should be case insensitive. Or I broke it. Got to return this.
JASON: So now I should be able to say Furby, Cyclops. We should be able to get Bop It.
Holy buckets, did that just work?
BEN: And that's it. Now you are using your computed property to dynamically filter through. It does the caching and everything for you. You don't even have to think about it.
JASON: That is really nice. So I think, then, I'm a little worried about time. So the next thing here is we were going to try to do votes. Maybe what we should do first is deployment.
BEN: Okay, yes. Let's deploy it.
JASON: So let's take this thing up. If we can get everything running, we will. But for now, we'll see how far we can get. So I'm going to use the UI. I'm going to go over here and stop my task. So that's stopped. I can reload. Good. Come out here, looks like my UI is still running, which is to be expected. Do I stop the UI service?
BEN: I usually just kill it in the terminal.
JASON: Okay, I wasn't sure if there was a button.
BEN: Then it says disconnected.
JASON: So now we have all of our stuff. If I look out here, let's use the Git UI. So we have this whole mess of things. It's ignoring all the stuff we wanted to ignore already. That's beautiful. So if I just Git add everything, then that puts us in our -- our app is saved. We've got images, the Babel config. No node modules, no Git folder, nothing we wouldn't want to save. Images are going in. Here are our components. Good. So then the other thing we would need to do is we'll commit this and say filterable list. Then we can push that. Look, I got GitHub dark mode.
BEN: Oh, nice. I just saw the commercial for this.
JASON: I'm really excited about it.
BEN: That is super nice.
JASON: Yeah, it looks really good. But yeah, so this is now up on GitHub, but it's not published. So to publish it, I have the Netlify CLI. Oh, wait. We need to do this differently because we have an app folder. So I'm going to first create Netlify.toml. That's going to end up in here. If I look in these folders, what we have in here is we're going to have to tell it where to go because it's not the main directory. So we can set the base to app. Then we'll make our command -- what's the Vue build command?
BEN: It auto detects most of it, usually. But yeah, it's build.
JASON: We'll go npm run build. Then we want to publish dist. We don't have functions yet, so I'll leave those out. And that should be good enough. So now that we've got that, you can see all those bits are there. So I'm going to Git add all of that. Git commit, and we'll say chore, add Netlify toml. Now when we init, it'll know to get to the app folder instead of trying to build and run from the root, which is where we were. So, let's Netlify init. We're going to create and configure a new site. We'll put it on my team. We're going to call this -- I gave it a name out here. I'm going to look at it before I get this wrong. We named it Vue gift list. Okay. Dist. And there we go. If I go out here, app.netlify.com, then it should be building. This, I think, will just take a few seconds because we just got to install the dependencies real quick. This is a good time for some hold music, chat. Hint, hint.
BEN: Well, I guess in this case, there is a question from Linda about composition API, which I think while we're waiting it might be a good time to answer. I'm sure those who have followed Vue 3 have heard about the composition API. So if I were to give an elevator pitch of what composition API is, today we've worked with the options API. It basically has you --
JASON: You're getting trolled. Sorry. That was not directed at you. That was directed at me. (Laughter)
BEN: It is all good. So what we saw and built with Jason today is the options API, which is what most people know from Vue 2. It gives you a convention for how to build Vue apps. As we saw, data goes here, computed goes here, and later we'll be seeing methods. Basically, composition API takes that and says, here, here's an open block, and then write it however you want. So in that regard, I think, people who are familiar with React will be probably a bit more comfortable because it's a little like hooks in that regard. So you have to manually define your data, your computed, those sort of things. So we actually will have an episode on Learn With Jason on composition API. I think it's next month.
JASON: Yeah, I think so. Let's go take a look. It is coming up on January 19th. You'll be coming back. That whole episode is going to be built around the composition API. So that's going to be a whole lot of fun. Make sure you mark your calendar. Remember, you can get the Learn With Jason schedule add the to your calendar, if I remember correctly, here. Let me check this first. I think it's lwj.dev/calendar. Nope, not that one. Oh, god. What is it, everyone? There it is. Okay. It is Jason.af/lwj/cal. That'll be a great time. So now we have this, and we have about 25 minutes. Do you think we can get a database up and running and updating in 25 minutes?
BEN: Yeah, we got this.
JASON: All right. I believe it. I believe we can do it. Let's get Hasura going. I'm going to pull up another browser for Hasura because I'm already logged in. That will make us a little faster. So let's go to cloud Hasura projects. Then we're going to create a new project. This is not an episode on Hasura. We do have one. I would highly recommend checking that out if you're interested in what we're doing today. Okay. U.S. East Ohio. That's the free tier. This is all going to be set up for free. We're going to use Heroku so our database is also automatically configured for us for free. Create this project. Okay. So then, I'm going to go in here and we're going to add -- once this finishes initializing. Doo-doo-doo.
BEN: So I guess in the meantime, for those wondering, the options API and composition API are both equally valid. It's not have a the composition API replaces the options or vice versa. We recommend doing what's best for your team.
JASON: Nice. So I'm going to set up an admin secret. I'm going to use something that no one will ever guess. Ha-ha! Okay. So I've got this admin secret. Now that I've got that, I can launch this console. I'm going to go into -- come on, internet. Today is not the day. We're on a schedule here. There we go. Here we go. We're moving.
Oh, you can do it. Come on, little compooper.
JASON: All right. Getting authorized. Internet is killing me right now. Okay. So we're going to go to this data tab. Come on. Come on. Here we go. We're going. Hello? Anyone? Hey, thank you so much for the sub. Okay. So now I'm going to create a table. We're going to call this table -- what did we call it? We'll call it votes. That seems fine. And we're going to vote for an ID. That's going to be -- how did you key these?
BEN: They're just integers, I think.
JASON: Integers. No, they're --
BEN: Oh, sorry. The ID. You can do whatever. Obviously we can replace it.
JASON: Whatever. I'm going to store them as text. We'll make them unique and that's fine. Then we'll do votes. And that's going to be an integer, and the default value is zero. All right. Primary key is going to be IDs. I'm going to add that table. Then once we have that, we will have a basic setup for our -- once this reloads -- votes. Now we'll be able to search for the ID and votes for an individual thing. Right now it's going to be an empty array. What was that? What was that button? Why did that happen? We're going over here. Hiya! My internet just decided to like really slow it on down today. So what we'll get back is this data. So I'm going to name this one wish list votes. That will be how we store our votes. Then if we want to create votes, we will -- I'm going to move this out, actually. So we need two things. We're going to create a folder called functions. I'm going to create this in app because we set our base there. And in this, we're going to say load votes, and that one is going to use this query. Then we also need one called add votes. We got 20 minutes on the clock. Can we do it?
BEN: We got this.
JASON: Okay. So then to add a vote, I'm going to use a mutation. What we will do is we will insert votes one. Hmmm, can we update columns increment? Or do I need to do update votes -- yeah, we'll do update votes by primary key. So we're going to update votes by primary key. This will be interesting to see if this works on a nonexisting piece. So let's grab one of these. We'll say here. We want to increment votes by one. Then we'll just get back the ID and the votes. What this will do is it will say, I want to update the votes by its primary key. That's what by PK means. The primary key is the ID. We want to use its ID, which is whatever we set in our JSON database. Then we're going to increment that by one. If we're lucky, this isn't going to explode. Is that going to do it? I'm not sure if this is going to work, because it might need to exist before it'll run. If it needs to exist, then I'm going to insert these really quickly so we don't have to figure out how to make that work.
BEN: Yeah, makes sense.
JASON: Come on, y'all. Oh, Hasura, you're killing me right now. That progress bar is a lie. Oh, no.
BEN: Oh, no.
JASON: Did we hit an outage? That would be a sad end to this stream. (Laughter) I guess we'll find out shortly.
BEN: We can always sub the vote if we need to, for now.
JASON: That's true. We can always sub it. Sad trombone. Okay. So votes by PK. I'm going to try this one more time. If this doesn't work, we'll just sub it. I'm going to update votes by PK. Increment by one. We'll get back the ID and the vote. So let's run this. And it doesn't work because there is no data in the database.
BEN: Yeah, it doesn't exist.
JASON: So to fake this, we can actually just go in here and insert these rows.
BEN: Sounds good.
JASON: And to do that, I'm going to pull up a database here, close that, and I can grab this one. And we will insert. So, boom, save. That'll initialize it one because we set a default value. Then do it again. Save. Is it doing it? Yes, it is. Okay. So then we've got -- I might only do like three of these. We can sub the rest out.
BEN: Yep, makes sense.
JASON: Oh, good clip, Chris. That'll be a sound effect. Okay. So we have our first three. The pizza shooter van, the cyclops, and the Furby. Everything else will break if we try to vote on it, but that's okay. If we run this mutation, it will update -- this is the pizza shooter van. Now it has a vote of one. If we run it again, it has a vote of two. Okay. So that's the way we want this to work. We're going to take this out here. We will blow this up, head over to add votes, and we're going to do one quick thing, which is to make this a -- what did we call it? We called it text. Instead of using a hard-coded value, we'll use a variable. Okay. So to send these, what we can do is we're going to have a serverless function. So we're going to exports handler. That will give us an async function. Or we're going to declare an async function. In this case, we're going to get the event. We will post an ID so we can get that out by JSON parsing event.body. I'm going to use a little helper I've written for doing Hasura stuff. Otherwise, it takes a long time to get all this done. And we want to get this done quickly. So let's get -- this is a work in progress of the new Learn With Jason site. But in it, I have Hasura utility. And what this does, is it creates an ability to use -- to send a Hasura Hasura request. It uses a fetch API to the Hasura URL and makes a post request with the Hasura admin secret and the body, which is the query and variables. So this is our GraphQL query. This is any variables we need to send. Then if it fails, it'll return empty. Otherwise, it'll return our data. So I'm going to copy this -- how we doing on time? Oh, 15 minutes. We can do this.
BEN: We can totally do this.
JASON: Then I'm going to create a new file. We'll paste this in here. That means I need these values. So I'm going to go over to Netlify. And here in my site settings, I'm going to go to deploy, environment, edit variables. I'm going to drop -- whoops. Not that. I'm going to drop the Hasura URL in. That Hasura URL is going to match our explorer API rate here. So I'm going to copy this. We'll put that into here. Then we need that Hasura admin secret. And you'll never guess it. We've got those set. So I can come back out here. Now that we've got those set, these are -- oh, I need to do one more thing, which is install node fetch. So I'm going to go into app, and we'll npm install node fetch so that Hasura utility works. Okay. Now that I've got that, I'm going to go out to add votes. We're going to import Hasura request. That comes from util Hasura. Okay. Then down here, we can say const data equals await Hasura request, and we're going to pass in a query and variables. Our variable is going to be the ID. Our query is going to be this. Okay. So that is the mutation part. Then we're going to return that. We'll return a status code of 200, and the body will be a stringified data. Okay. So, happy with that. I'm happy. Then we've got to do the same thing over here, so I'm going to copy/paste the whole shebang. I'm going to get this. We don't need an ID anymore because we're getting all of them. And we don't have any variables, so I'm going to pass in an empty object. And that should give us all of our wish list votes. So let's test this. I think we should be able to test it by going out here and running Netlify dev, I think. Nope, doesn't like it.
BEN: We are in app, I believe.
JASON: We are not in app, but it doesn't like -- no server detected and no command specified. Interesting.
BEN: You have to package that JSON as an app, though, right?
JASON: Oh, is it? Starting a development server. Then we can go to Netlify functions. What did I call this? Load votes.
BEN: Yeah, load votes.
JASON: That's not right. What? Is there some kind of a universal redirect we missed?
BEN: We only have one package JSON, right? There's not another one.
JASON: Oh, I think it might be --
BEN: Do we just want to move everything out then? Just drop it out.
JASON: Yeah, that's probably going to be the easiest thing. So let's do this. I'm going to -- what is it, copy app to here.
BEN: Oh, nice. I literally just said VS code.
JASON: I think that'll do everything. Let's find out.
BEN: It looks right. Oh, wait. Did it not move?
JASON: I don't know. Still moving. Oh, god. Oh, god. What have I done? I should have deleted node modules first.
BEN: Oh, that's right.
JASON: So we've still got the Git, the Git ignore. Everything is here. Then I think if I update the Netlify.toml -- oh, boy, ten minutes. We can do this, we can do this.
BEN: We can do this.
JASON: I'm going to take this out and add a functions directory. Okay. Then we're going to run Netlify dev. This is going to work on the first try. Okay. Function server is listening. Come on, come on, come on.
JASON: It didn't immediately redirect. That's good. My internet. Oh, man. My computer sounds like it's going to take off. We might be timing out because Hasura is going slow for us today.
BEN: Oh, that's right.
JASON: Oh, Hasura. I think we might be out of luck because we got to wait for that outage. Whatever the delay is, it has to get cleaned up.
BEN: Then we can at least show how it would work.
BEN: So this is where we can learn about the events with Vue. So here -- yes, to do. Sounds good.
JASON: And we can do that as an update later. In the meantime --
BEN: So for those who have -- so, V-on is the explicit way of doing it. Typically it's done with the at symbol. This is what you're more likely to see inside Vue apps. That's how you know it's an event. If you scroll down into our script block, below computed, we're going to define another option called methods. So these will be basically a series of functions that we allow Vue to track and reference. So we called it, I think, submit vote.
JASON: Submit vote.
JASON: Then does it also need to be a function?
BEN: This also needs to be a regular function if you want to refer to other properties inside of --
JASON: And this is event like usual?
BEN: It does get an event like usual, if you need it. But you can also do a payload as well. Those are options. But typically, you would basically go like -- oh, you want to prevent default? No, no, no. Jason, we don't do that in Vue. You can delete that line because what we're going to do is if you go up to the top -- so yeah, delete the prevent default. Scroll up into the HTML. Events have modifiers. If you do the dot prevent on the click, it will then automatically prevent default. And just save. That's it.
JASON: Hm, okay.
BEN: So modifiers are a common way to do these common things that happen. Whether you want it to only -- the event to trigger once, double click -- or not even double click. There are other modifiers. But they're supposed to take care of those common scenarios. Oh, a common one is key up. You can do dot enter so it only fires the key on enter so you don't have to manually do the statement. So that's where the modifiers come in.
JASON: Yeah, and so what we're going to need to do on this is we would need to send the ID in, which we don't currently have. If I need to pass the ID into submit vote, how would I do that?
BEN: So next to submit vote, do the parentheses, open it up, and then we're going to drop in the gift dot id. I think that should be it.
BEN: That should be it. We have to check real quick, but it either will override the payload or -- but we'll see. If we just console log, it'll tell us. I usually have to do this every single time.
JASON: So now we can come back out here. I can open up -- where's my regular local host? Here we go.
BEN: Go back, I guess.
JASON: Then open this. Console log a wish.
BEN: It should click something.
JASON: It's not doing anything. Okay. Let's debug. We've got like two minutes to debug this.
BEN: Yeah, we do have two minutes to debug. So we have local server running.
JASON: I think so. I can't imagine this wouldn't be running otherwise. Because we're running Netlify dev. Does that matter?
BEN: No, it shouldn't. I haven't run into an issue with that. So just to the plain text. Forget pulling out the payload.
JASON: Wait. No, that seems right, doesn't it?
BEN: These are all warnings.
JASON: Okay. What did you want me to do?
BEN: Instead of pulling out the ID, just do console log hello. It feels like something is not pulling. The prevent shouldn't be doing anything.
JASON: That's definitely right. Hello.
BEN: So it is refreshing, but if we scroll up -- we are assigning it on the button. Remove the prevent for now. Submit vote.
JASON: And I don't need to do one of these?
BEN: Hm-mm. It knows what to look for. Take out the payload. Let's make it even simpler. Save. What is going on?
JASON: I'm worried that it's serving the -- I'm worried that it's -- oh, what is it? Is it develop?
BEN: Oh, run serve.
JASON: I'm a little worried that Netlify dev is not configured to actually run serve. So some of the hot reloading or something is missing.
JASON: So it's 8080. Nope, not that.
BEN: This is bewildering.
JASON: So we've got the button click.
BEN: Just make sure the text is changing. Yep.
JASON: What's broken? What did we do? Oh, my god. I know what we did.
BEN: Oh, we're in the wrong VS code. The file is in the other app.
JASON: See what we get for trying to be clever at the last possible second? Okay. I'm going to copy this one. I'm going to move out to this one. Save. Now I broke something.
BEN: ID is defined but never used. That's fine.
JASON: Okay, that's fine. So we'll just -- the ID is in here.
BEN: Oh, yeah. Take that out for now and save.
JASON: Okay. So let's try all that again. So we're going to do gift. What a -- ugh. Then we should get the ID. Then we would get the ID. And there it is. Nice. So then what we would do is we would send a fetch to our serverless function.
JASON: And that would allow us to update the votes. And with that, unfortunately, we're out of time.
BEN: Yes, that we are.
JASON: But I'll tell you what, what we can do with this is we will probably clean this up. I imagine that either Ben or I will turn this into an article or something to finish that up. But yeah, cool. Ben, where should people go other than your Twitter if they want to find you online?
BEN: Yeah, you can find me at Bencodezen.io. I think I cleaned all the stuff. Like YouTube. I'm even on TikTok now, believe it or not. So BenCodeZen on all the things.
JASON: Excellent. So go check out Ben. Make sure you check out Vue. Remember it's V3.vuejs.org if you want to use the Vue 3 stuff we did today. Definitely, definitely go play with this Vue CLI and Vue UI. What a great experience that was. Make sure you go and check out our schedule. We've got some really, really fun stuff coming up later this week. Emma Bostian is coming on. We're going to argue about Taco Bell for 90 minutes. Lindsey Levine is coming on. We're going to learn how to play Minecraft. We're literally going to play the game Minecraft. It's going to be amazing. Then Jay is going to come on. We're going to do something real fun. So, with all of that, please go mark your calendars. Remember, you can find us on all the lwj.dev stuff. Go add that to your calendar. Remember that our show is sponsored by Netlify, by Fauna, by Auth0, and by our new sponsor Hasura. So make sure you go and check them out. And the captions today were provided by Rachel at White Coat Captioning. With that, Ben, thank you so much. Chat, thank you for hanging out. Stay tuned. We're going to go raid someone. We will see you next time.
Closed captioning and more are made possible by our sponsors: