Connecting Nuxt 3 to the Contentful blog
In order to make sense of the output of the headless Contentful CMS we can use the SDK that Contentful provides. In the project, you can install them via the CLI using:
npm install contentful @contentful/rich-text-html-renderer @contentful/rich-text-types
This makes our job a lot easier!
Create a plugin
Let's first create and register a plugin for using Contentful with Nuxt. Create an empty file called `contentful.server.ts` in a `plugins` folder in the root of the project and paste these contents in the file:
import { defineNuxtPlugin } from '#app'
// for dev
import { createClient } from 'contentful'
// for SSR, SSG
import contentful from 'contentful'
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
const createClientFunc = process.env.NODE_ENV === 'development' ? createClient : contentful.createClient
const client = createClientFunc({
space: config.private.CONTENTFUL_SPACE_ID,
accessToken: config.private.CONTENTFUL_ACCESS_TOKEN,
})
nuxtApp.provide('contentfulClient', client)
})
Here you see Nuxt using the values from the runtimeConfig from previous step. We're telling Nuxt here that we want to be able to use the `contentfulClient` as plugin on the nuxtApp.
Next up, we are going to build a composable which lets us reuse our logic across our application. Paste the contents of this example in a file called `contentful.ts` in a `composables` folder in the root of your project:
interface Image {
title: string;
description?: string;
file: {
url: string;
details: {
image: { width: number, height: number },
},
},
}
interface Page {
title: string;
body: object;
heroImage?: Image;
};
export const usePage = async (slug: string): Promise<Page> => {
const { data } = await useAsyncData('page', async (nuxtApp) => {
const { $contentfulClient } = nuxtApp
return $contentfulClient.getEntries({
content_type: 'page',
'fields.slug[in]': slug,
limit: 1,
})
})
const { title, body, heroImage } = data.value.items[0].fields;
return {
title,
body,
heroImage
}
}
This file exports the `usePage` composable and it does little more than fetching an entry from Contentful with the type `page` and matching a slug parameter. Then it returns an object with a title, body and heroImage.
💡 At this point, if you haven't done it already, please create a Page in Contentful with the slug `home`, because let's show a homepage on screen!
We need some new files now. First, we're going to create the actual page. Create a `compontents` folder in the root of your project and past the following in a file called `PageBody.vue`:
<script lang="ts" setup>
/**
* Define props
*/
interface Props {
slug: string;
}
const props = defineProps<Props>();
// ----------------------------------------------------------------------------
const { title, body, heroImage } = await usePage(props.slug);
</script>
<template>
<article>
<header>
<h1>
{{ title }}
</h1>
</header>
<div>
{{ body }}
</div>
</article>
</template>
We're using the Composition API with script setup using TypeScript, which I think is a neat way of defining props.
As you can see, we use the `usePage` composable here, to return information to the component. Now we need to get the `slug` into this component. That's our next step.
Quickly create a layout which we can reuse later. Create a `layouts` folder and add the following contents to the `default.vue` file:
<template>
<NuxtPage />
</template>
The file system routing allows us to control what component gets loaded on what URL implicitly by folder structure. Create a `page` folder to tell Nuxt where the routing should start with. Now create an `index.vue` file to correspond to the root path of your application (basically your homepage). Add the following contents:
<script lang="ts">
definePageMeta({
layout: "default",
});
</script>
<template>
<PageBody slug="home" />
</template>
Finally, we'll register all of this in the Nuxt app instance. Replace the contents of the `app.vue` file in the root folder with this content:
<template>
<NuxtLayout />
</template>
If you run `yarn dev` you should see the raw JSON content of whatever you added in Contentful. Congratulations! 🎉
By the end of this step, you should be at this stage of the project.