skip to content

Let's Learn Nuxt!

If you’re building apps with Vue.js, Nuxt is a powerful way to get up and running quickly! In this episode, Maya Shavin teaches us how to combine Nuxt with Cloudinary for high-power, high-speed development!

Full Transcript

Click to toggle the visibility of the transcript

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

JASON: Hello, everyone, and welcome to another episode of "Learn with Jason." Today, on the show, we have Maya Shavin. Thank you so much for joining us.

MAYA: Thank you for having me. It's so great to be here.

JASON: Yeah, it's going to be a great day. I am really looking forward to this because we are going to be learning something that I don't know and using something that I kind of know and really enjoy. I don't know Nuxt really at all. We did an episode way back in the day where I tried to figure it out. I did okay, but I didn't get far. We're going to use that in combination with Cloudinary, which is one of my favorite tools in dealing with images on the web.
Before we talk about all that, for those of us who weren't familiar with your work, can you give us a little bit of a background on yourself?

MAYA: I'm a senior front end developer in Cloudinary. I was working mainly on the dev, which is the console application for Cloudinary.

JASON: Okay.

MAYA: Recently, I started working more on the development compatibility, like building all the tools for Cloudinary in the Nuxt plugin, which is the work I've done. Whatever you ask for to make your life easier with Cloudinary is my job.

JASON: Nice, nice. I feel like that's sort of my favorite place to be as a developer is in a position to just try to make things easier for other people. I love that. How do you like that job? Is it fun?

MAYA: It's fun. It's super fun, and it's also very challenging. Let's put it this way. You deal with a lot of different aspects, the different user requirements, and the developer has a lot of requirements.
(Laughter)

JASON: That's like the politest way of describing it. Yes, absolutely.

MAYA: I'm trying to be polite.

JASON: Yeah. Today, we're going to dig into Cloudinary, and we're going to look at how we can use that with Nuxt, and specifically a getting started with Nuxt. Especially for myself, I have done very, very little with Vue in general and almost nothing with Nuxt, so I'm really excited to get my hands dirty and just kind of learn what it's capable of, what we can do with it.
Why would someone use Nuxt? What are its big, big feature points?

MAYA: Well, first of all, the developer experience is super easy to get started. It's very easy to understand how to work with it because it builds on top of Vue. View is well known for being easy to learn and has a lot of recommendations. If you love Vue, Nuxt is definitely something you should check out.

JASON: Awesome, awesome. Well, yeah. I'm super excited for this because I've seen a little bit of Vue stuff. Every time I see it, I love the approachability of it. The single file component approach always makes me happy. I love that you get to put everything together without having to change syntaxes. I like that I just get to write CSS, but I don't have to put it into a separate file or wrap it in a template tag. It's a really nice developer experience. And so, what is your favorite what drove you to write Vue in the first place? What got you to start writing it?

MAYA: Well, that's an interesting question because I work mainly with React.

JASON: Oh, cool. All right.

MAYA: It's kind of funny. I knew Vue before I started with Vue because I had to migrate a whole project from Angular to Vue. I was in charge of choosing which framework to migrate to. React doesn't migrate. In the end, I haven't finished that migration, but I joined Cloudinary. Cloudinary pulled me back to React.

JASON: Oh, okay.

MAYA: I'm kind of in the middle. Basically how do I say it? I like React and I know it, but my heart belongs to Vue.

JASON: I think that's a really good way to say it because I like a lot of I really like working with a lot of tools, but ultimately, I have my preferences. My heart belongs to Vue is a great way to put that. I think that's really nice.
I'm actually really excited to just go ahead and jump right in, so let's switch over into the pair programming view. While we do that, I'm going to do a quick shout out to our sponsors. We have live captioning of the show at learnwithjason.dev/live. The live captioning is being provided White Coat Captioning. Thank you to Tess, who is with us today. White Coat Captioning is possible for us because of the generous sponsorships of Netlify, Fauna, Sanity, and Auth0, which pitch in to make this show more accessible.

MAYA: The transcript is great.

JASON: It makes me really, really happy. This is also available on the show when it gets posted tomorrow. Also, make sure you go and follow Maya on Twitter.

MAYA: Yea, follow me.

JASON: Definitely, definitely go follow Maya. Lots of good information there. Lots of helpful information, and a great person to reach out to if you have Cloudinary questions.
Today, we're going to be working with Nuxt, so I'm going to drop a couple links here. We're going to be working with Cloudinary, and I found out right before this call there's a whole library specially built for using Cloudinary with Nuxt, so we're going to dig into that a little bit. If I want to get started, what should my first step be?

MAYA: First, you need to create a Nuxt project. You can get to "get started." A devi from the Nuxt team made a great effort in changing the Nuxt documentation, so it is so great. Shout out to Debbie, if she's in the chat. I'm not sure if she'll be here today.

JASON: Let's see. We've got node. I have node. I have VS code.

MAYA: At least version 10? Yeah.

JASON: Let's check which version I'm running. 14.

MAYA: Okay, cool.

JASON: Should I use "create Nuxt app"?

MAYA: Yeah, create Nuxt app.

JASON: Yarn, create Nuxt app. We'll call this "let's learn Nuxt." While we're waiting for that, we can go take a look at what happens next. We're going to move into it. We can just start it right up. Oh, wow.

MAYA: There's a little bit of configuration. You need to set it up beforehand, but it is very fast. It depends on how quick your downloading is going to be. You know how it is with npm packages.

JASON: Oh, come on. Do it. I'm on the good internet. This should be real fast. Let's see. Here we go. Get my 27,601 packages.

MAYA: Wow. Okay.

JASON: Should we use JavaScript or type script for today.

MAYA: Not type script for today. I have enough type script for the whole week (Laughter)

MAYA: I spent a whole two days to figure out how to set the type up correctly so it would not scream at me.
(Laughter)

JASON: Okay. In the UI framework, do we want any UI framework today?

MAYA: If you like tail winds, you can go for it. Nope. Maybe content. Let's do content. It's all up to you. If you want it to scream at you every time you type something, then go for it, ESLint.

JASON: I'll add prettier so it auto formats on save. Let's do that. We're not going to cover testing today, so I should probably leave that out.

MAYA: Yeah. Okay. Yes, universal. Universal means services are rendered on the static side.

JASON: Static.

MAYA: Yeah.

JASON: I know I do. Do we need any of these, do you think? Recommended for VS code if you're not

MAYA: I normally choose that one.

JASON: Okay. Continuous integration. We can set it up with Netlify later if we get there. Version control. We use Git.

MAYA: I have this configuration. I don't want to choose everything. It takes so long to set up a project. But when I set up a project, it always wants to have some kind of configuration on the CLI. It kind of conflicts here.

JASON: Yeah.

MAYA: Out of the box, no.

JASON: It's really cool that it lets you kind of get all those pieces preconfigured. I like that it makes them optional. One of the things I don't like about starter kits is when all I want is to get started quickly and I end up with somebody's opinions instead of just the ones I needed. I remember with a lot of React projects where you would start and you would to go in and remove a bunch of things because there was all this boilerplate you didn't need. It's kind of a pain to have to make all those decisions, but it is way nicer than having to go in and remove those decisions from the code later on.

MAYA: Right. I agree with you. I still remember how terrified React was.

JASON: Nikki, I don't think the create scripts can be run again unless Nuxt is doing something special. I think this is kind of like a you make a decision and that's final, right?

MAYA: Right, right. If you run it again, it will throw an error or it will override the whole project from the beginning. Yeah.

JASON: Okay. This is actually running now. I can do the getting started doc set. I can run yarn dev, so I'm just going to do that. That runs the Nuxt command. Am I interested in participation? Sure. I'll send you some data.

MAYA: Oh, that's new. I didn't know that.

JASON: Now I think I can go to local host 3000. I'm going to copy/paste this while we wait. I like that. That's nice to show the progress. Good.

MAYA: Debbie was telling me they're thinking of showing some tips and tricks on the waiting screen. Or a random joke in the middle of waiting.

JASON: I like that. That sounds fun.
(Laughter)

JASON: Computer has decided to beach ball of death. I really hope that's not going to do what I think it is going to do.

MAYA: Are you having everyone has a Mac, right?

JASON: Yes.

MAYA: Me and Debbie were stuck in the middle because Mac decided to take a break.
(Laughter)

JASON: It looks like we're okay. I just closed my other browser windows to make sure I don't get into trouble. We are now looking at the default page. This looks really nice. It's my project name. It's got a link to the documentation and a link to the Nuxt GitHub. Sweet. Okay.
What should we do next?

MAYA: Okay. We're talking about Cloudinary, so let's install the Cloudinary module for Nuxt.

JASON: Here's the documentation for that. Cloudinary.Nuxt.JS.org. Drop another link in there. I'm going to install this one. @Nuxt.JS/Cloudinary.

MAYA: You can just go to the setup, I think, and then just copy it.

JASON: Perfect. Okay. Let's stop here and install this. Let's look at the config. It says, add Nuxt.js in the module section of Nuxt.config.js. Here's that. We've got modules.

MAYA: Go to module.

JASON: Let's bring this up here and delete the rest of it. Okay. Prettier is helping. Nuxt.js Cloudinary is now installed as a module. What does this plugin do?

MAYA: Well, it does a bunch of things. First, it allows you to make the URL for a public ID and a home transformation on the fly. It also allows you to upload a resource on the build tab or on the private side depending on what mode you're using. It has a bunch of components, the Vue component, you can use. You can have a template. There's a lot of things. Let's just start with the simplest thing first.

JASON: Yeah. We need to add a Cloudinary config. My cloud name is JLengstorf. We can add that. Then I'm going to add a link to this so we can find it again. Now, once this is set, it says we're all set. Great.
At this point, we've set up a whole site. Now the "create app" kind of did the work for us. I'm just going to take a peek in here and see what we've got. The assets folder is empty. The layouts folder has a default. There's an empty middleware folder, no plugins, no store, favicon. We looked at this Nuxt.js. Everything else is kind of boilerplate.

MAYA: Right.

JASON: It looks like this is where we start. We've got our index. This is what we're looking at on the home page, right? I think if I go back out here and I restart the site let's play this way. I'll get this one set up here and this one set up here. Hi, chat.
Once this finishes loading, it will hot reload for us. You good? There it goes. Now we have the ability to just kind of edit in here. This is kind of what I was talking about with Vue being single file. There's a template tag and a script tag and a style tag. These aren't magic. React has a specialized syntax. Is there anything aside from the fact that these single file components exist are we writing a JSX style thing or is this is plain JavaScript or this is plain CSS, this is plain HTML?

MAYA: Yes. In template, you write HTML. In the script, you write JavaScript. In the style tab, you write CSS, but you can write SCSS. It really depends on which one you want. If you add in the scope to the style, this will already be scoped to this page and the CSS selector will not be available for the other page.

JASON: With the D.

MAYA: Yeah.

JASON: Wow! Okay. If I'm understanding this correctly, that means, when I go to look at this, all the styles in here where are they? That's the error page. Which one of these is the actual look at that. This is the scope part. It adds a data attribute so it only affects wow, that's really cool. Okay. That's super fun. It does look like it didn't like maybe I just need to reload. Oh, I just have to reload. The hot reload didn't add the data attribute. I just had to reload for that. Holy crap. That is nice. We're writing class and not class name. It feels like HTML, but Vue does have some specialty stuff, right? We can do loops and logic and stuff like that?

MAYA: Yeah. We can do loops on the template itself. Let's say you have a JS query array. You can use the VU4 attribute on the setup tab. Let's do a test so people can understand what we're talking about.

JASON: Yeah. How do I get an array?

MAYA: Okay. For the Vue component, you need to have the data. Data is the function that will create a new instance of data. Then it will return an object. You can say images. What's an array you want to name?

JASON: Images will be great because we can use it to show off the Cloudinary stuff.

MAYA: Right.

JASON: Do I need to list when we do this with Cloudinary, am I providing a list of the public IDs? I guess we can look at this real quick. Let me get my Cloudinary console up. Cloudinary.com. I'll get logged in. I'm going to pull this off screen real quick while I log in. Okay.

MAYA: Okay. Yeah, in Vue, we can use stack components. We have our library Vue stack component. It can write JS inside the script tech. Unfortunately, it's not working with Vue tree yet. Yet. Almost any other plugin or library for Vue that we actually need to change to be more compatible with tree.

JASON: Okay. I think I have somewhere in here how do I just open this? Open? Store. Here we go. This will help. These are the images that are currently

MAYA: Oh, a Corgi. That's cute.

JASON: I never talk about this. I always forget it exists, but I do sell stickers. These are the images being used here. The "Learn with Jason" is using Cloudinary.

MAYA: That's so cute.

JASON: These are the public IDs. This is what the image is identified by.

MAYA: Sorry. Just a correction. It's the public ID, but you need to add the folder before to make it a full public ID.

JASON: Good call. This is in LWJ/store. The full public ID is going to be LWJ/store/rubberCorgi.

MAYA: Right.

JASON: If I take this and I go out here and I say, I want my images, we'll do LWJ/store like this. Is this how I use the Cloudinary Nuxt plugin or do I need to do it a different way?

MAYA: No, this is how you use it.

JASON: Okay. Perfect. Let's grab a few. I'll grab the Corgi pal. Let's grab one more. We'll grab the boop. Okay. Now we have three images. If we want to loop through these, how would I do that?

MAYA: Okay.

JASON: I'm going to remove some stuff here actually just to give us a little more

MAYA: You can remove the documentation stuff there.

JASON: Let's drop all that out. We're going to work with just this part. Okay. My to do, loop through images. If I want to do that, what should I do?

MAYA: Okay. Let's create a container, a different container, that will contain all the images. You can use UI. I like to use div. Image, like an image stack. Then do SRC for source no, no, no. Sorry. Go back before. V for.

MAYA: V for.

JASON: Got it. Then images. That's this, right?

MAYA: This automatically knows this is pointing to that one. Then we need a key. Let's do dot dot key.

JASON: Key like this?

MAYA: Yep, like that. Equals image, because we have the image. And source. Dot dot source.

JASON: Oh, like this?

MAYA: Is that the correct yeah. For now, we can put the image inside because we want to see if the image we created will loop. Then we'll generate the URL for Cloudinary.

JASON: Okay. Do that. First, if we save, we get a bunch of broken images. That's expected, but we can see that it created three images. Each one got one of the values from our let me make this smaller again one of the values from our array. In order, rubber Corgi, Corgi pal, boop. Perfect. This is the Vue sort of syntax. If it's a specialty Vue thing, it is going to be a V dash. Am I correct in assuming this is like a shorthand?

MAYA: This is actually the key. It's not a short I don't think it is a shorthand, but it is more like it is like key in React when you have to loop in React. You have the key attribute. It is the identifier for this component so it will know when to rerender it.

JASON: Got it. Nikki in the chat is asking if that's equivalent to V bind key, like this.

MAYA: Yes.

JASON: Okay, cool. Great. Either one will work. It looks like this is a great shortcut for us. This is great. I can visually look at this, and it makes sense to me. I can see I'm getting one image out of images. I'm know that images is down here. It's all in the same file, so I can find the stuff that I'm doing. That is nice. That's really nice. Okay.
If I want to make this into a Cloudinary image, how do we do that?

MAYA: Okay. When we installed the Cloudinary module, in the background, it created an instance for each and every component in the Nuxt project. In the source, you can write dollars sign Cloudinary dot image dot URL.

JASON: Okay.

MAYA: And open bracket, put image inside.

JASON: Oh, okay. This is going to take a public ID into this URL helper function and generate the full URL for me.

MAYA: Right.

JASON: Oh, that's cool. And there we go. It works. Wow, that was so much faster than I expected it to be. That's amazing. Okay.

MAYA: Yea.

JASON: This is great. We've got this container class. I should probably call this something else. Let's call it image container. Then down here I can do I think I dropped all of these.

MAYA: Did I just see a boop just pop up?

JASON: Yeah, I have a boop that's built with a library called matter.JS. Whenever someone uses the boop emoji in chat, it causes one to drop from the top of the screen. Chat, if you want to

MAYA: Oh, cool.

JASON: demonstrate, please spam us with boops for a little bit.

MAYA: I've got to do it. I cannot find it. Where is it?

JASON: That one is only for subscribers.

MAYA: Oh, I see.

JASON: Okay.

MAYA: Let me subscribe.

JASON: We can probably do this as display grid and maybe grid template, columns. We'll do a repeat 3, 1FR. We'll do a dot image container image. We'll make that max width 100%, which I believe will cause this to do what we want. There it is. Now it will fit and it is responsive.

MAYA: Oh, my God. Now it's really full of them.
(Laughter)

JASON: Oh, good. Thanks, chat. You never let me down. I know my bot sub expired. I've got to fix that. I'm in the process of rebranding. I was calling it stream blitz for a long time. Now I'm calling it socket studio because stream blitz was taking. I have to go switch over all the things. I just haven't had a chance. Soon that will work.
In the meantime, I also want to point out a few things. First, I love display grid. It's magic, but I also like in Vue but default I'm just writing CSS. I'm not having to import a CSS solution. I'm not having to install something or learn a new syntax. I'm just writing the CSS that I was writing anyways in my plain HTML files, but we get these added bonuses of having them scoped and having the CSS and the JavaScript and the HTML all in one place and being able to reference it right there. It's a really nice flow. I think it's really pleasant to use.

MAYA: And if you want to be more organized or if you prefer to separate them, you can also separate them in three different files and import them together into one.

JASON: Oh, cool. Okay. Yeah. As the code base gets more complex, that probably starts to make sense, right?

MAYA: Right.

JASON: But yes, this is very, very cool. I'm legitimately blown away by how quickly we were able to get that all integrated. We are 30 minutes in, and we've already set up a Nuxt site. We have already integrated Cloudinary. We're already pulling images from my Cloudinary site along with a bunch of messing around. What a great way to get up and running fast?
What else should we look at here?

MAYA: Sorry. You know what one of the nice things about Vue that we haven't used yet, that we haven't used a reactive property.

JASON: Let's do it.

MAYA: Okay. Let's add a field called computed.

JASON: Computed. I add that outside of data, right?

MAYA: Yeah, outside of data. Next view. It's an object.

JASON: Object.

MAYA: Now you put Cloudinary image.

JASON: Plural like this?

MAYA: Yep.

JASON: Okay, okay. That's a function.

MAYA: It's a function, but it cannot be like that. It has to be the other one because we need the scope.

JASON: Got it. Okay. We'll do it like this.

MAYA: Not like this.

JASON: No, not like this.

MAYA: Yeah. Just a function inside the other function.

JASON: It's been so long since I've written it this way that I've actually forgotten to do that.

MAYA: You're too used to React, I guess.

JASON: Too used to React.

MAYA: Every time when I switch to React from Vue, I'm like, oh, no, I want to change. When I'm in React, I want the Vue things.
(Laughter)

MAYA: Okay. Let's do it. Return. You're going to return. We can do this dot images. Then you do map.

JASON: Map, okay. Then we'll get an image.

MAYA: Yeah. We'll just use the same code that we used for generating the source code, but we'll add the source URL.

JASON: Oh, oh, okay. With this, now I don't have to run this up here. I can instead do Cloudinary images like this?

MAYA: Yeah.

JASON: And then we can just go back to a plain ole image.

MAYA: Yep. Then just use image.

JASON: Oh, that's really slick.

MAYA: It makes it more clean. You need to return it, I think.

JASON: Is it this dot?

MAYA: This dot. This dot. If you do it inside, you have to do this dot. Still need to do return, I think.

JASON: Oh, that's right. That's right. Oops.

MAYA: Yeah.

JASON: There we go. Okay. All right. I've completely forgotten how to write nonReact JavaScript as evidenced by this.
(Laughter)

JASON: Yeah. Now we're here. We're getting the same result, but our markup is a little bit cleaner, which I think is nice. I like the idea of not calling functions in my attributes. This is nice because this looks like what I would expect. We're running a little bit of business logic as JavaScript, not in line. That's nice and clean.

MAYA: Right.

JASON: Okay, excellent.

MAYA: The nice thing about reactivity of Vue is you mentioned something like a variable inside with this, that attaches to the data. If the variable changes, then this whole computed property will be coded again. It will try to compute the new value of it. It's done automatically.

JASON: Nice.

MAYA: That is the local state in React.

JASON: That means I should be able to I could put an input in here and choose another public ID and it would add it to the list?

MAYA: Yeah. I think so.

JASON: Should we give that a try, do you think?

MAYA: Why not?

JASON: All right. Let's do it. I will adjust a form element. Inside of that, we'll do an input. We'll call it type text. We'll give it an ID of "sticker ID." We'll make it CamelCase. I didn't do any Vue things. I just set up some basic markup, but what I think this will give us is the ability to just enter any sticker public ID. We want that to the add to the array. If I'm going to do that, how would one go about doing that?

MAYA: First, we need to attach it with the event you want to click on the button to submit it.

JASON: That's probably easier, right? That way we don't get a bunch of broken images while we type.

MAYA: In input, we need to attach to the data at the local state, a local variable. Let's just go back to the data and declare a new variable. Probably input.

JASON: Input. That's better. Leave it empty to start?

MAYA: Yes, leave it empty to start. When you go up to the template, we're using something called V module model. My English. I've been staying home for so long.
(Laughter)

MAYA: Equals to input.

JASON: Oh.

MAYA: You can actually tell it to do two way binding with the input variable with the input field. Whatever value that we type inside the input, it will automatically put it into the variable.

JASON: That's really slick because even with the extra ergonomics of something like React, like React hooks, you still need a few lines of code to make that work. The fact that this is just declare it here and link it up here and it's done, that's pretty slick. That's really nice.

MAYA: Right. Now we need to add another function to attach to the click on the button, right?

JASON: Okay.

MAYA: In Vue, all the functionality will be added inside an object called metals. Let's declare metals on an object inside. Metals with an S.

JASON: Like that?

MAYA: Metas

JASON: Methods. Sorry. We've got methods. Is it any name we want?

MAYA: Yeah, any name you want. Like declaring a new function.

JASON: Like this?

MAYA: Yeah.

JASON: Okay.

MAYA: Then you can do this dot images and push the sticker.

JASON: Wait, we called it input, right?

MAYA: Oh, input. Yeah.

JASON: We can check if this input dot length is greater than zero. Okay. Do we have to handle a NO OP case or it will just do nothing?

MAYA: This input is an array.

JASON: No, it's a string, but if we do

MAYA: Oh, right, right, right. Yeah. Normally, I use the input

JASON: Like that one? Oh, we can also do that. Like that, you mean?

MAYA: Yeah.

JASON: Sure. I'm up for whatever. I think that one might just be whatever we prefer. It's probably going to be fine in this case.

MAYA: Actually, it's fine anyway. Let's attach the on click

JASON: Is it

MAYA: Submit. This sign.

JASON: Like that?

MAYA: Yeah. Submit equal. Then put the function image.

JASON: Okay.

MAYA: You can write V dash equal 3 dash submit equal something, but this is the shorthand version of it.

JASON: Nice. Cool. This is equivalent to this?

MAYA: Yeah. If you want to make sure it will not escalate propaganda oh, my God, my English. Propagate. I feel so ashamed with my English. It isn't usually so bad.

JASON: I can barely speak English, and it is the only language I can speak, so you're killing it.

MAYA: Every time I speak, I have three different versions of translations in my head. I have to choose the right one to know which one I have to go back. Don't be surprised if I throw Hebrew in the middle of the conversation and Vietnamese.
(Laughter)

MAYA: Vue actually provides a shorthand for that, so you can do submit dot prevent. I think it is submit dot prevent.

JASON: Wow.

MAYA: Then it will not go into public

JASON: That's legit. How cool is that? Of course, when you're doing JavaScript, you're very frequently going to be preventing the default action of click events. That's what writing single page apps is all about, taking over browser behavior. How cool is that to build that right in?

MAYA: If you want to submit only one, to trigger the event once, you can do submit dot once, I think.

JASON: Like all together?

MAYA: Yeah.

JASON: Wow, this is really, really cool. That's really nice. I think I need to add some light styling here because it looks like it deleted a lot of my stuff. Let's go up here. We'll add a class of input and a class of button. Down here we can do input and a border of one pic solid black. For the button, we can do color white. We'll do a background of black. That's probably good enough. Let's see how that works. Add a little bit of padding. That's a little rough to look at.

MAYA: Then it is asking what things do you miss from React when writing Vue? A lot of things. Hooks.

JASON: Hooks, yeah.

MAYA: Yeah. I like the JS exit. It is much easier when you write everything inside together so you can fix it. You don't have to scroll up, scroll down, scroll up, scroll down to go to the template to fix it. Composition API, yeah. What else? A bunch of other things in React. Redux.

JASON: Yeah. I think it is just really cool I love that we're actually spoiled for choice. I think one of the things that always pushes innovation is React came out with hooks and then Vue adopted the best ideas of that into the composition API. Vue's single file components have driven a lot of innovations in the React space to try to keep that similarly good DX. I think everybody borrowed the best ideas from Angular. I think it is really, really interesting.
I really like the idea of the community working to share the best ideas, push each other forward, make sure we don't get away with settling down and getting lazy and forcing people to do things that are bad because there's no competition. I love that developers are trying everything and pushing each other forward.
Now, if this works, I should be able to add a fourth sticker just by is that the right thing? I can't remember if that's the actual prefix. I've forgotten now. Here's our images. LWJ/store. Here goes nothing.

MAYA: By the way, Vuex is definitely better than redux. Sorry. Opinionated.

JASON: We're here to learn. We're here to have opinions.
(Laughter)

JASON: It worked. Look at that. It worked on the first try, which that's rare.
(Laughter)

MAYA: This is not something that you'd be surprised about. Most of the time it works on the first try until you add more conflicts.

JASON: There we go. This is really nice. Oh, the once we shouldn't have used.

MAYA: Oh, yeah. We should remove the "once" because then it will only allow you to click once.

JASON: We can do that. Maybe we can do down here instead is if there's an input, we'll set it. We'll set this input to empty.

MAYA: Right.

JASON: Now, if I reload this let's try it. We'll do LWJ/store learn with Jason. Now it is empty. LWJ/store/bearded Corgi. Excellent. We've now built a full input/output thing where we've bound data in Vue to a dynamic array so we can compute that using Cloudinary to get images. And these images are all hosted on Cloudinary. This is one of my favorite things about Cloudinary. Everything is done through a URL. I have my cloud here, and it is all hosted on res.Cloudinary.com. There's a couple basics.
Do you want to talk about what these are?

MAYA: This is in the Nuxt Cloudinary module. We add something by default. This is the default when you use the URL function in the Cloudinary module. It also means it will fetch the format. When it returns it to you, it will be automatic, which means Cloudinary will detect which browser it is and what kind of format will be most optimized, optimal for the asset. In WebP, it will return a WebP. That's done automatically. You don't have to do anything for that.

JASON: And we can see here that this has happened. This asset, if we go and look at the Corgi, this was uploaded as a PNG. If I take this out I'll take both of these out then we can see I got back an image PNG. It is 34.5 kilobytes roughly. If I go back and reload, it is 13.8 kilobytes. Web and we got back an image WebP.

MAYA: If you're in mobile, you don't need a big resolution because in mobile the screen size is very limited. Calculating the correct resolution is not that easy. When you send it to Cloudinary, Cloudinary will know the device of the user, what's the resolution, what mobile device. Then it will return the image with the quality that looks the best on that device. Also, your size will be much smaller.

JASON: Uh huh. Yeah, yeah, yeah. Yeah, this is really nice, right? Now we've got all of that. We are loading images. They're being automatically optimized, automatically sent in the right format. I saw your question about Safari 14.

MAYA: I gave me a JPEG too.

JASON: I don't know if I've updated Safari in a very long time. It might be that I'm using an outdated version of Safari. If anybody in the chat wants to try that, I'll give you a link so you can try that.

MAYA: Someone in the chat asked about AVIF. In Cloudinary, you can use AVIF. You can try it with AVIF and it will return to you the AVIF one. Maybe.

JASON: Henri is in there testing. Lots of cool things. Lots of interesting opportunities here.
Now what we've done is we have built a Nuxt page. One of the things that's interesting about Nuxt is that Nuxt gives us the ability to do static generation or server side generation. We chose the static mode. What does that mean and how is that different from regular Vue?

MAYA: Sorry. First of all, Nuxt is server side rendering. Everything you saw right now, when it deployed, it is server side. It prerenders the static HTML, and then it is served to client side. It's not only Vue. It's Vue on top of Vue.

JASON: Oh, okay.

MAYA: Static side means it will not do anything like that. It will actually view everything in static and pregenerate all the routes you have and then deploy Netlify. Then this way you already have static.

JASON: This, what we've just done with all the computed fields and all this dynamic stuff that we've done, we can deploy this to Netlify?

MAYA: Yeah, I think so. Yeah.

JASON: Let's just give it a shot. Let's see what happens. There is no Git remote. Let's Git create "Learn with Jason," let's learn Nuxt.

MAYA: Someone is asking if Nuxt is a copy of Next. They're both server side rendering. When you type in Nuxt on Google, it will autocorrect you to Next.

JASON: What I'm doing right now is deploying this to Git so we can get it up on Netlify real quick. Let's call this a work in progress. Trying out deploys. Then I'm going to Git push. We're going to set the upstream, so I don't have to keep typing it out, to origin main. I've changed my defaults, but my brain is still learning the defaults.

MAYA: I wonder if the Netlify CLI will show up.

JASON: I have the Netlify CLI installed, so I'm going to run Netlify init. I'm going to create and configure a new site. I'm going to go to my account. We'll call this "let's learn Nuxt." It's now created a site. My build command, actually don't know my build command, so let's look. To build, I need build. We will run yarn build.

MAYA: Generate.

JASON: Generate. I'll have to go fix that then.

MAYA: Sorry. I wanted to catch you.

JASON: The directory to deploy, do you know where it generates to? Is it public or dist or something like that?

MAYA: I think it is dist.

JASON: That's built. I think I'm going to leave Netlify open. Then build and deploy. It is going to ask me for all my stuff. I'm going to change this to generate. We'll save it. Then I can go back out here. It says that it is building, but we know it is going to break because it ran the wrong command. Instead I'm going to go back out here and just start a new one. Hopefully, this should be nice and quick.
Running build, that's for if it is going to run on a server, right? It will be expecting to go to node. Generate is what takes all of that and runs it through static side generation so it becomes static assets. Is that correct?

MAYA: Yeah.

JASON: Excellent. This first build will be a little bit slower. We've got to install all our node modules and all that good stuff.

MAYA: Also, the nice thing about Nuxt, the new version, is it is two separate processes. If you make some change on the content let's say you have the content separated. It will already know the code hasn't changed. If you use the catch Vue, it will shorten the second deployment on building the static side.

JASON: Oh, nice.

MAYA: It will generate a static HTML.

JASON: Excellent. Okay. We're running our Nuxt generate here. I'm not sure how long that is supposed to take. Let's run it locally and just take a look. It's got some Cloudinary stuff. Oh, okay. It's done.
Here's our site. There we go. It's live. We can do the same thing. LWJ/store. We'll just add the boop again or LWJ/store again bearded Corgi. We have all the interactivities there. We've got the ability to add more stickers as we go. This is nice, and this is all running in a way if I do view source, we can see this is in the HTML. I can come out here and actually disable JavaScript. Let's do that, if I can find it. Where is it? Disable JavaScript.
I won't be able to add these because we're using JavaScript for that, but the core of the site works. This is the appeal of static side generation. We're getting to a point where the majority of the site is going to work. If we're serving a blog or marketing pages, if we're serving an app, we can get a lot of content that doesn't change between views to somebody without requiring any JavaScript, and then we can use JavaScript to enhance it so that we get extra capabilities. That's really nice.
What should we do next, do you think?

MAYA: Well, the nice thing about Nuxt is you can actually fetch dynamic content before you serve on the site. It will still work when you disable JavaScript on the client side.

JASON: I would love to give that a try.

MAYA: Let's just add something called async data.

JASON: Like out here?

MAYA: Yeah. Async data is the function that will be called when on the header, like before the page is served to the client. It will get all the data and external API code and the external things that you want to have, and it will not run on the client side. Async data comes before the component is generated. You don't have the dist. If we see an object called context if the object context is the parent parameter, you can do the same. You can use the same image.

JASON: This image?

MAYA: Not this image. Just create an array of an image. We don't have dist here.

JASON: I have my images. Then am I returning this?

MAYA: Yeah, you can return that.

JASON: Okay. Then I've got an array.

MAYA: Basically, returning this one means you return the local state, the data for the local state of this component.

JASON: Instead of returning it we'll return it in both?

MAYA: We can do both. If you're using async data, you don't have to do the other one. The other one is to declare you have a field that contains images.

JASON: Got it. Okay. If we wanted to, what we could also do is send out a call I don't know if we have fetch installed here, but we can do images equals await fetch.

MAYA: Something like that we can also do.

JASON: Get my images or whatever it would be. That would run. Maybe actually we can just do that real quick to show it. Here is an API that I love because it is very fast. They give us the ability let's use the rest one this time. That's not what I wanted. We want the characters. We can get one character. We'll get back an image. We can just show that one image. Let's grab this character. If we do just this one, that will give us a single character we can use. Can I make this async?

MAYA: Yep. You need to add the async on the front. Yeah.

JASON: I should be able to do Rick equals await fetch. Let me make this bigger so we can see what we're doing.

MAYA: Basically, this one will return the new data. Then Nuxt will merge both the data from the async data and the local component itself together.

JASON: Oh, interesting. To start, I probably just want to do it like this because this is going to break our Cloudinary stuff.

MAYA: Uh huh.

JASON: We can maybe just see what happens if we do this. Does that seem right?

MAYA: Let's take a look.

JASON: Okay. If I want to display this let's just dump the data. Do I need to do anything special? Can I do this?

MAYA: Yeah, you can do it like that.

JASON: Okay. Let's try and see what happens. Is this running? I think that's running.

MAYA: It's already run, right?

JASON: There it is. Oh, look at that. Now we have our image. We can pull that out and do something like was it just Rick dot image?

MAYA: It is an image with a URL. Now you can use a fetch URL if you have the fetch.

JASON: That's what I was thinking. Now that we've got this, we can do let's move this to the bottom. We'll kind of work in descending order down here. Container template. I've got to move this. We'll have an image, and the source will be the Rick dot image, I think it is. Yes. The alt will just hard code to be Rick Sanchez. That should give us an image. Nope, that one is broken. What did I break? Get me in here.

MAYA: Oh, you need to have a dot dot.

JASON: Yep, that's my bad. I forgot.

MAYA: Yeah, that's something I also miss in React. In React, you can just write it and do the bracket. In Vue, if you miss the dot dot, good luck.

JASON: I wonder what happens if I double container this. Is this going to explode? Tune in to find out. Div. Don't need whatever that was. Oh, exactly one root. Never mind. Can't do that.

MAYA: Oh, right. Right. In Vue, you have to have one root. Vue tree already supports two roots. Don't quote me for that. I don't remember exactly.

JASON: And I'm just going to bump this down to be display block with 100%. Is that going to take it out of the flow? What are you wrapping? What did I do? Is this display grid or something?

MAYA: In the container, you have a flex display in the container.

JASON: Got it. How about this? We'll say flex direction column. That should go vertically. There we go. Okay. Now we've got our Rick image that's being pulled in, but we can see this is being pulled in from here. First of all, we're hot linking to the Rick and Morty API. It's better to not do that. We don't have any of the optimization stuff that we're getting on our other images, whether they're being auto formatted or auto quality.
Can you show me what we would do with this remote image if I want to pull that in? How can we use that?

MAYA: Okay. In the Cloudinary module, we have something called a fetch remote. All you need to do is call an image dot fetch remote, pass the URL, and whatever transformation you want to have. That will do all the work for you.

JASON: Okay. Can I do it in async data or do I do it in computed data?

MAYA: You can do async data. You can do it in async data also. You don't need the computed one.

JASON: It would be Cloudinary

MAYA: Context dot dollar sign Cloudinary.

JASON: Lots of things just clicked in my brain. In async data, context is taking the place of this?

MAYA: Yeah, exactly.

JASON: Got it. It was Cloudinary dot image?

MAYA: Image, yeah. Fetch remote.

JASON: Like this?

MAYA: Yeah. Just pass the URL inside.

JASON: Okay. Then I need to rewrite this because it is no longer Rick dot image. It is now just Rick. Okay.

MAYA: Okay. Let's take a look.

JASON: Okay. Now this has been fetched. I'll open this in a new tab. We can see that it is on Cloudinary. It is using my cloud. Instead of image upload, it is image fetch. We still got the format auto, quality auto, but we just passed in the URL.

MAYA: Right.

JASON: This is slick. What this means, if I understand correctly, is I have effectively said I want to upload this image, but the URL has ceased to matter. This website can now go down, but I would still be able to use this URL.

MAYA: Right. Exactly.

JASON: That is slick, and we're not hot linking anymore. We're not hitting the Rick and Morty API and taking their bandwidth. We're using our own Cloudinary account. We're optimizing it.

MAYA: And that's fetch.

JASON: What if I want to modify this a little bit? What if I want everything on the site to be grayscale? Can I just do that?

MAYA: Yeah. You absolutely can do that. Just add an up check with the effect that you want. That's an additional parameter in the fetch remote. Second param. Just an object. You want to grayscale, right?

JASON: Yeah.

MAYA: Let's do an effect and grayscale.

JASON: Like that?

MAYA: Yep, just like that.

JASON: And that's all?

MAYA: Uh huh. Exactly.

JASON: Okay. Let's go back out here and look at it. There we go. Grayscale Rick.

MAYA: Bingo!

JASON: This is the thing that made me fall in love with Cloudinary. Notice we're still doing F auto, Q auto, but we added this grayscale and it just works. We can do all sorts of stuff with this. If I want to make it smaller, I can do width 50. Now we have a little one.

MAYA: Right.

JASON: What are some fun ones? What are your favorite effects?

MAYA: Wow. My most used one is to make it into a round circle. R max.

JASON: Like that?

MAYA: Yeah. Let's add a bit of crop thumb. You have a width of 50.

JASON: Look at it. That is beautiful.

MAYA: Basically, what it does is it crops as a thumbnail on your face. It will detect the face.

JASON: Oh. Let's go look at an actual face. Here's a media library. I think somewhere in here I have pictures of my face. I have so much nonsense in here. Here's a picture of Marissa's face. This is a good one. Let's crop on Marissa. If I go in here and I say it goes after the upload, right?

MAYA: Right.

JASON: I create a block. You said R max, C thumb. Let's just make it width 200. Oh, no.

MAYA: You need to put a high width.

JASON: Oh, because it automatically set all that detail. Height 200.

MAYA: That one is not exactly correct, right? To change it a bit, let's do G face I think it is G face.

JASON: I think that sounds right. G face. G is for gravity. Is that right?

MAYA: Yeah.

JASON: Look at that. How cool is this? This is the part that blows my mind. If we're trying to do author photos or we're trying to aggregate data and get something up quickly that looks kind of uniform, by setting this gravity face, I didn't have to go and manually edit all of these different things, all these different images. If I had 50 speaker photos or whatever and they all uploaded to a form, this means in addition to then I can set the Q auto and the F auto. Now what I've done with this is I have styled my speaker images. I have made them into thumbnails. I have focused on the author's face. I've set the right size. I've optimized for speed and format, and all of that was done by just setting this in my URL. We can set all of this through here as well if we wanted to?

MAYA: Correct. We can do gravity. Yep, face. Width 200. Radius

JASON: Just max, like this?

MAYA: Yes, just max.

JASON: Do we need anything else?

MAYA: Maybe we need crop thumbnail. I don't remember.

JASON: This one looks okay. That one is doing what we want. I think we might have needed the crop thumb. We can do Marissa's as well and just copy this whole thing and put one in here. Let's get the original version.

MAYA: Just to answer, someone asked does running the static generation create another Rick on Cloudinary? No, it only creates one.

JASON: Can I just do this with a regular image, like an image URL?

MAYA: You can do that, but in the object, you need to pass type. Wait, this one is already Cloudinary, so never mind.

JASON: I think this will just work. Let's try it. Then I'm going to go up here, and I'll just duplicate the Rick. We'll change this out to Marisa.

MAYA: Thumbnail. It is trying to crop it at the thumbnail. If your picture is clear enough, it will crop most of your body and your face. If you have something like Marisa with a newspaper and the newspaper is bigger, it will just crop the newspaper instead.

JASON: Yeah. This is so much fun, all the different things you can do. There are things you can do with this that are really powerful that we're not touching on. You can add text overlays. You can do interesting color things, like add gradients or do two tones. It's super, super fun.

MAYA: In Cloudinary, I have a page with a favorite overlay. I have one I overlay on top of me. Maybe I can find it.

JASON: I don't know if this is going to do what I want, but let's see. Yeah, that looks better.

MAYA: Let's try this one real quick. This is the one that I'm using.

JASON: Okay. Let's get that one pulled up.

MAYA: Anna, Cloudinary is free.

JASON: Oh, wow.
(Laughter)

JASON: If you want to get into Cloudinary, you can get started at this link, I think. Jason.af/Cloudinary. That will just let Cloudinary know you saw it through this show. It doesn't change anything else. It's still free. I think Cloudinary gives me a small monetary compensation in result for using that link. If you're thinking of Cloudinary, clicking that link helps me out a little bit, which is nice. This is blowing my mind with how awesome it is.

MAYA: You haven't seen the other one. There were people that do a whole video of themselves with an overlay of a video on a picture. This is an overlay of a gif on a picture.

JASON: What you've done here is you have added two images together. You're doing all of that through the API. We start with this Sailor Moon. Is it the L that brings in another photo?

MAYA: Yeah, L means the layer, which means you start with an L underline, and then you put the public ID of the other photo. That will put another layer on top of your image.

JASON: Uh huh.

MAYA: And then width. Then you have effect plural. You can see it is kind of 50% blurry in this case. If not, it will be on top of each other, so then we would not see my face.

JASON: Yeah, this is really, really cool. I'm very, very blown away by how much you get out of the box, both with Nuxt and how we're able to let's go ahead and deploy this. We can Git add everything. We'll Git commit and say, add fetched images and effects. Let's get this thing live. I'm going to Git push. Back on our Netlify page, we'll see that it picked that up on the push, so it is going to start building for us. Here in probably a minute or two we'll have a new deploy of the site.
In the meantime, I think that's a great stopping point. Let's start sharing some links and stuff. Make sure you go and follow Maya on Twitter, like I said. You can get started with Nuxt on the Nuxt website. Here's a link to the getting started docs. You can get started with Cloudinary at Cloudinary.com or I'm going to drop my link one more time. It is Jason.af/Cloudinary. Make sure you go check out the Cloudinary.Nuxt.JS module that is super fun. Another quick shout out to the sponsors, Netlify, Fauna, Sanity, and Auth0, which enables us to bring in White Coat Captioning to do live captions, which makes the show more accessible. It gives us the ability to include transcripts with the show when we post it on the website, and I'm just so grateful that enough people get enough value out of the show that sponsorship is even in the realm of reality.
Maya, if somebody wants to go further, where would you recommend they go from here?

MAYA: With Nuxt?

JASON: With anything. Just any helpful resources with Nuxt, clouds Cloudinary, anything like that?

MAYA: If you want to study Nuxt, we have Vue school or Vue master. Let me take a look.

JASON: Is it Vue mastery?

MAYA: Vue mastery or Vue school. They both have a great class. Vue mastery is more for beginners. If you don't know about Nuxt and you want to start, this is a great resource for that. Vue school has basic and more advanced courses on Nuxt, so you should definitely check it out. This is a great resource for study. I'm not sure about frontend master. There's some course in frontend master, but I'm not so familiar with that yet.

JASON: That is a great question. Let's see if anyone has done Nuxt hey, look. It is Sarah.

MAYA: Is she in the chat right now?

JASON: I'm not sure if Sarah is in the chat right now, but she's an excellent teacher. One of my favorite teachers in the world.

MAYA: Me too.

JASON: If you have a frontend master subscription, you should check this one out. It is going to be very in depth, very good.

MAYA: If you want to do something like pluck or documentation, like the one you saw with the Nuxt Cloudinary module, you should check out the content module of Nuxt, which will give you out of the box features. It is super great.

JASON: Maya, what if people want to keep up with you? We've shared your Twitter. I'm going to drop that link one more time. Where should people go if they want to keep up with you?

MAYA: You can go to my Facebook or my GitHub. I'm not so active on Facebook page.

JASON: Okay. Is it the same username on GitHub?

MAYA: Yeah.

JASON: Okay.

MAYA: On GitHub.

JASON: All right. Yeah. This has been so much fun. Thank you so much for taking time out of your day to come hang out and teach with us today. Any parting words? Anything else you want to share with us before we go?

MAYA: Well, Nuxt is awesome. You definitely should check out Nuxt. If you like Nuxt and great Gatsby I like all of them, but let's go Nuxt. We need more Nuxt. Nuxtifier. Nuxt and Cloudinary is awesome. I'm working on more things with Cloudinary.
We have a Nuxt image component similar to the Gatsby image component. That is on the way. I hope I'm allowed to share that.

JASON: Yeah, absolutely. We love secrets on this show.
(Laughter)

MAYA: I just hope Debbie will not send me a message and say, oh, no, Maya, you're not supposed to tell that. I'm working on a data layer, GraphQL layer, for Cloudinary.

JASON: Nice. That is super slick. I'm so pumped to see all of that, and we'll have to talk about having you back on to talk about the GraphQL layer because that is so much fun. I had an absolute blast. I hope you had fun. Chat, I hope you had a great time. Check out the schedule for upcoming shows. We have a whole bunch of really excellent content coming up. We are going to have Lindsey Kopacz come on the show and teach us all about building an accessible audio player in React later this week. That one is going to be super fun.

MAYA: That's cool.

JASON: I'm so excited. It's going to be great. Lindsey just dropped an ebook that is all about writing accessible code. I'm really, really excited to have her come on. We have Adam Barrett coming on to teach us about static site workflows with NX. I thought it was only for repos, but there's a whole lot more. David East is rejoining the show. We're going to do a multiplayer sound board. This one is going to be wild. By the end, we want everyone to be making noise on each other's laptops as we all work together on this one. This one is going to be especially fun to tune in live. There's so much fun stuff coming up. I really want to see everybody here.
With that, I'm going to call this one done. Maya, thank you again. Chat, thank you so much. Stay tuned. We're going to go and raid Shirley Woo, who is live right now. Thank you so much. We'll see you next time.

MAYA: It was so much fun. Thank you very much. Bye bye.

Learn With Jason is made possible by our sponsors: