Integrations
v1.0.0+
@nuxt/content
While using @nuxt/content module for content fetching and displaying, you may want to automatically optimize the main cover image for a single post out of the box. You can do this by using Cloudinary upload feature and modify the fetched document with content:file:beforeInsert
hook.
To use $cloudinary
inside any content hook, you need to add @nuxtjs/cloudinary
before @nuxt/content
in the modules
property.
Assume structure of your content
folder is as below:
-| content/
----| posts/
-------| hello-world.md
-------| hello-world-cover.png
----| index.md
And in content/posts/hello-world.md
, we define the following YAML front matter block:
title: Hello World
image: hello-world-cover.png
description: A hellow world post
In which image
points to the image file hello-world-cover.png
located in the same directory.
To start optimizing the cover image defined in image
field, we need to perform the following inside the handler for content:file:beforeInsert
hook:
-
Check if the target image has been uploaded to your Cloudinary account according to its unique public id. We choose the public id according to the syntax -
{post-file-name}-cover
.If the image has been uploaded previously, retrieve the image's information with
$cloudinary.explicit
. -
Else, upload the original image file to Cloudinary with the target public id defined in step 1, using
$cloudinary.upload
. -
Replace
image
with the returned information.
import path from 'path'
export default {
modules: [
'@nuxtjs/cloudinary',
'@nuxt/content',
],
cloudinary: {
cloudName: process.env.CLOUD_NAME,
apiKey: process.env.API_KEY,
apiSecret: process.env.API_SECRET_KEY,
},
hooks: {
'content:file:beforeInsert': async (document) => {
if (document.extension !== '.md' || !document.image) return
const { $cloudinary } = require('@nuxtjs/cloudinary')
const publicId = `${document.slug}-cover`
/* Get existing image from Cloudinary based on publicId */
let asset = await $cloudinary.explicit(publicId, {
type: 'upload'
})
/* There is no image uploaded yet, so upload and save it */
if (!asset) {
asset = await $cloudinary.upload(
path.join(__dirname, `content/posts/${document.image}`),
{
public_id: publicId,
}
)
}
/* Replace image with the return object */
document.image = asset || {}
},
}
}
On the page component - posts/_slug.vue
for displaying a single post, we can use CldImage
to display the uploaded cover image responsively and lazily.
<template>
<article>
<h1>{{document.title}}</h1>
<cld-image
v-if="document.image.public_id"
:alt="document.title"
quality="auto"
fetchFormat="auto"
responsive
loading="lazy"
/>
<nuxt-content :document="document" />
</article>
</template>
<script>
export default {
async asyncData({ $content, params, error }) {
const slug = params.slug || ''
const document = await $content(`posts/${slug}`)
.fetch()
.catch(err => {
error({ statusCode: 404, message: 'Page not found', err })
})
return { document }
}
}
</script>
Check out CldImage documentation
Another approach is to use $cloudinary.image.url()
to generate the optimized delivery URL and pass it to a regular img
or picture
element. The returned URL will already contain basic optimizations (f_auto
for auto format delivered per browser, and q_auto
for auto resolution quality per device) by default.
<template>
<article>
<h1>{{document.title}}</h1>
<img
v-if="coverImage"
:src="coverImage"
loading="lazy"
>
<cld-image
v-if="document.image.public_id"
:alt="document.title"
quality="auto"
fetchFormat="auto"
responsive
loading="lazy"
/>
<nuxt-content :document="document" />
</article>
</template>
<script>
export default {
async asyncData({ $content, $cloudinary, params }) {
const slug = params.slug || ''
const document = await $content(`posts/${slug}`)
.fetch()
const coverImage = document?.image?.public_id ?
$cloudinary.image.url(document.image.public_id)
: ''
return { document, coverImage }
}
}
</script>
Check out Image optimization documentation