Nuxt Image Is Out, and So Is Its Prismic Integration!
Nuxt.js is one of the popular frameworks to build websites with Vue.js. That's great, because at Prismic we love to build websites — especially with Vue! Ahem. We do handle the data while you build the website. But you got my point! (and we do build websites with Nuxt and Prismic anyway...)
One of the key features of Nuxt is its huge module ecosystem. Thanks to modules you can easily extend Nuxt's behavior however you like — from adding an automatic sitemap generator to controlling your lights while developing. You can think of Nuxt modules like WordPress plugins. And you'll often add too many of them to your site, but Nuxt won't break when you do updates. (Just kidding, we love you WordPress!)
We're talking about modules today because last week Nuxt announced the release of a new official module: Nuxt Image. In this post, we'll discover what Nuxt Image does, then we will go through how to use it with Prismic by improving a gallery website with responsive images, and more.
What does Nuxt Image bring?
The Nuxt team built Nuxt Image to optimize images on Nuxt websites. It handles the hard parts of transforming and optimizing assets through an intuitive interface. But that's not all. Nuxt Image also comes with two interesting features.
The first feature is that Nuxt Image is provider agnostic. This means that by learning how to use Nuxt Image, you learn how to optimize your local assets, but also images from Cloudinary, TwicPics, and many more — including Prismic! Nuxt Image does this in a smart way by leaving the provider in charge of transforming the images it's providing. This means that Nuxt Image won't impact your build time. For example, with images from Prismic the transformation would still be done by Imgix (Prismic's image provider).
The second cool feature from Nuxt Image is that it doesn't output some unexpected markup while serving your assets. Want an img tag? You'll have an img tag. That's it! The module comes with two components: nuxt-img and nuxt-picture. They are drop-in replacements for their native counterparts: img and picture tags. We can use them to apply transformation and make our images responsive without overhead.
But, instead of explaining more here, let's dive into it and see how to do it with Prismic, shall we?
Usage with Prismic
This tutorial assumes you already have basic knowledge of Nuxt and Prismic. If you have any doubt, you can check out our documentation.
Alright! As teased above, we will optimize images on a simple gallery website. For that purpose, I went ahead and created a simple website with Nuxt and Prismic. You can download the starter source here if you want to follow along. And, if you're curious here's what the end result looks like (you can also browse it on Netlify):
A quick tour
Before diving in, we'll have a quick tour of what's going on in our project. (If you feel at ease feel free to skip this section.)
Here we have a regular Nuxt app created using create-nuxt-app. The files interesting to us are:
- package.json: it has Nuxt and Prismic installed already;
- nuxt.config.js: it's the default one, but if you scroll a bit you can see that we registered the Prismic integration for Nuxt in the buildModules array. We also configured it just after. To follow this tutorial you can leave the Prismic configuration as-is;
- ~/pages/index.vue: it has all the code rendered on our website with some CSS. In this tutorial, we'll only have to deal with the Vue template. You can ignore the script and style part of the component.
With all of that in mind, we're ready to get started! Let's install dependencies with npm install. We can then launch our dev server with npm run dev, it should open on port 3000.
Setting up Nuxt Image
To set up Nuxt Image we first need to install it as a devDependency to our project. Let's do so using our favorite package manager:
npm install --save-dev @nuxt/image
# alternatively if you're using yarn
yarn add --dev @nuxt/image
Once the package is installed we can now head to our nuxt.config.js file and register our new module:
export default {
/* ... */
buildModules: [
"@nuxtjs/prismic", // already here
"@nuxt/image"
],
image: {
provider: "prismic",
prismic: {}
},
/* ... */
};
And that's it! We made the integration of Prismic with Nuxt Image as straightforward as possible so we don't have to provide any custom configuration for it to work. All we need to specify is a prismic key with an empty object ({}) for Nuxt Image to know that we're using Prismic.
In the above snippet, we also specified the provider option with "prismic". This is used to tell Nuxt Image that we want to use Prismic as our default image provider. In most cases, this should be fine. We just need to know that without it we would have to specify which provider to use when using Nuxt Image components (more on that below).
Optimizing the logo
The first image we'll optimize is the logo. It's a tiny asset that we allow our editor to edit within Prismic's writing room. Peeking at ~/pages/index.vue it currently uses the prismic-image component:
<prismic-image :field="data.logo" height="32" />
The issue with this image is that while we asked it to be tiny using the height attribute the image downloaded is still way bigger. To fix that we have two main solutions:
- We add a constraint to our image field inside our Custom Type in Prismic. The downside of that method is that adding a lot of variations to an image can add some unnecessary overhead to our content writers.
- We use Nuxt Image!
Because it's just a simple image we'll use the nuxt-img component, and it goes like this:
<nuxt-img v-if="data.logo.url" :src="data.logo.url" height="32" />
As we can see, it's pretty similar to using the prismic-image component:
- Instead of using the field prop we now use the src prop, but we still directly provide the URL from Prismic's image object.
- The image object from Prismic might be empty when the field in Prismic is also empty. To prevent unexpected behaviors we simply add a small check for it using the v-if directive.
And that's it! The magic of Nuxt Image is that it understood our height props, thus it's able to ask for an image sized accordingly. Now, our rendered image is coming sized properly 🎉
Optimizing the gallery
In the previous section, we went through optimizing a single image by having it downloaded with the right size. In this one, we'll see how to generate responsive images for our gallery.
Looking at the code we have a v-for loop used to render our images. Inside it we have another prismic-image component:
<prismic-image :field="item.image" />
Here again, if we peek at the images we can see that they are much bigger than what they need to be. Let's fix that using the nuxt-img component again:
<nuxt-img v-if="item.image.url" :src="item.image.url" sizes="md:380px lg:512px xl:50vw" />
Like last time we can see that we translated the field prop to the src one paired with a v-if directive for safety. What differs this time is that instead of specifying a single size, we specified multiples depending on breakpoints using the sizes props:
sizes="md:360px lg:580px xl:50vw"
This string means that:
- Up until the medium (md) breakpoint, our images must be 360 pixels wide;
- Then up until the large (lg) breakpoint, they must be 580 pixels wide;
- Finally, above the large breakpoint, images will be half the width of the extra-large (xl) breakpoint, that is to say, 640 pixels.
And without any additional code, our images are now responsive! As stated previously, Nuxt Image relies on standard HTML features to work. For example, to make our images responsive it uses a combination of the sizes and srcset attributes, which are now widely supported.
Want to make sure it's working? Alright, let's have a look at the network tab inside the dev tool. We'll then resize our window to small, give it a quick refresh, and then slowly scale our window bigger. Our browser should download new batches of images as soon as we get through bigger breakpoints! Hooray 🎉
Did someone say breakpoints?
Breakpoints don't come from nowhere. Nuxt Image provides some sensible defaults (the same as TailwindCSS), but we can always edit them to suit ours.
If we were to do so we could use the screens option of Nuxt Image inside our nuxt.config.js file like so:
export default {
/* ... */
image: {
provider: "prismic",
prismic: {},
screens: {
"mobile": 600,
"tablet": 800,
"desktop": 1200
}
},
/* ... */
};
And we could then use our custom breakpoints instead of the default ones like so:
<nuxt-img v-if="item.image.url" :src="item.image.url" sizes="mobile:380px tablet:512px desktop:50vw" />
Using multiple providers
We mentioned at the beginning that we don't have to set the provider option of Nuxt Image to Prismic. While in most cases we should be fine, there might be cases where we need to use multiple image providers and don't want Prismic as our default one. To mitigate that we just need to specify the provider to Nuxt Image components when using them with an image coming from Prismic, like so:
<nuxt-img provider="prismic" v-if="data.image.url" :src="data.image.url" />
Keep learning!
Summing up
Woo! We went through a lot! We especially focused on how to assign a fixed height for an image and how to make images in our gallery responsive. You can browse the final code of the website we optimized, and its production URL, on GitHub and Netlify:
Nuxt Image still allows us to achieve lots of other things. If you're curious about it check out the Nuxt Image documentation.
Of course, as Nuxt Prismic users, it's not mandatory to use Nuxt Image. You can still rely on our simple prismic-image component. Although it's nice to know that we now have a simple way to fine-tune even further our images if that's something we need.
A quick note before we end: in this tutorial we didn't use the nuxt-picture component. While it can become handy when we need to specify different formats for our images (png, webp, etc.), as Prismic users we don't really have a need for that. That's because Imgix, our image provider, already does so for free! That's what the auto=format part of the image URL is responsible for.
That's all! If you have any questions feel free to open a new thread on our community forum or to get in touch with me on Twitter, thanks for reading!