Nuxt.js with Stubby

Updated on

Learn how to seamlessly integrate Stubby CMS with Nuxt.js to create dynamic, revalidated blog content. This guide walks you through setting up Stubby CMS in your Nuxt project, enabling you to fetch and display content efficiently while leveraging the full potential of server-side rendering (SSR) and incremental static regeneration (ISR).

1. Setting up Nuxt.js with Tailwind CSS

Before integrating Stubby CMS, let's get a Nuxt.js project up and running with Tailwind CSS for styling.

  • Create a Nuxt.js project — If you don't have a Nuxt project, create one using the Nuxt CLI:
npx nuxi@latest init stubby-demo
cd stubby-demo
  • Install tailwind CSS — Add Tailwind CSS and its required dependencies:
npm install -D tailwindcss postcss autoprefixer @tailwindcss/typography
npx tailwindcss init
  • Configure tailwind in nuxt.config.ts — Add Tailwind CSS and PostCSS configuration:
nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  compatibilityDate: "2024-11-01",
  css: ['~/assets/css/main.css'],
  postcss: { 
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
  },
});
  • Tailwind configuration — Update the tailwind.config.js file to include all Nuxt template paths:
tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./components/**/*.{js,vue,ts}",
    "./layouts/**/*.vue",
    "./pages/**/*.vue",
    "./plugins/**/*.{js,ts}",
    "./app.vue",
    "./error.vue",
  ],
  theme: {
    extend: {},
  },
  plugins: [
    require('@tailwindcss/typography'),
  ],
};
  • Add CSS directives — Create an assets/css/main.css file and include Tailwind's base, components, and utilities:
assets/css/main.css
@tailwind base;
@tailwind components;
@tailwind utilities;
  • Run the development server — Start your project with:
npm run dev

2. Adding stubby CMS environment variables

Add your Stubby CMS API credentials to a .env file at the root of your project:

.env
STUBBY_API_KEY="your_stubby_api_key"
STUBBY_SITE_ID="your_site_id"
STUBBY_BASE_URL="https://stubby.io/api/v1"

You can find these values in your Stubby CMS site settings.

3. Setting up API endpoints

To fetch data from Stubby CMS, create API handlers in the server/api directory.

  • Fetch all pages — Create a server/api/all-pages.ts file:
server/api/all-pages.ts
export default defineEventHandler(async (event) => {
  const { STUBBY_API_KEY, STUBBY_SITE_ID, STUBBY_BASE_URL } = process.env;
  const url = `${STUBBY_BASE_URL}/sites/${STUBBY_SITE_ID}/collections?apiKey=${STUBBY_API_KEY}`;

  const res = await fetch(url);
  return await res.json();
})
  • Fetch a single page — Create another handler in server/api/a-page.ts:
server/api/a-page.ts
export default defineEventHandler(async (event) => {
  const { STUBBY_API_KEY, STUBBY_SITE_ID, STUBBY_BASE_URL } = process.env;
  const { slug } = getQuery(event);

  const res = await fetch(
    `${STUBBY_BASE_URL}/sites/${STUBBY_SITE_ID}/pages/${slug}?apiKey=${STUBBY_API_KEY}`
  );
  return await res.json();
});

4. Installing Markdown rendering support

  • To render content from Stubby CMS, install the nuxt-mdc module:
npx nuxi@latest module add mdc
  • Update your nuxt.config.ts file to enable SSR and configure the module:
nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  compatibilityDate: "2024-11-01",
  devtools: { enabled: false },
  css: ["~/assets/css/main.css"], 
  ssr: true,
  routeRules: {
    "/blog": { isr: true },
    "/blog/:slug": { isr: true },
  },
  modules: ['@nuxtjs/mdc'],
  postcss: {
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
  },
});

5. Building the blog pages

  • Main blog page — Create a pages/blog/index.vue file to list all blog posts:
pages/blog/index.vue
<template>
  <main class="container pb-40 pt-16 mx-auto max-w-[65ch]">
    <h1 class="text-3xl font-semibold mb-10">Blog</h1>
    <ul class="flex flex-col space-y-10">
      <li v-for="page in data" :key="page.metadata.slug">
        <nuxt-link :to="`/blog/${page.metadata.slug}`" class="font-semibold text-lg">
          {{ page.title }}
        </nuxt-link>
        <p>
          {{ page.metadata.description }}
        </p>
        <NuxtLink :to="`/blog/${page.metadata.slug}`" class="mt-3 inline-block text-blue-700">
          Read more &rarr;
        </NuxtLink>
      </li>
    </ul>
  </main>
</template>

<script lang="ts" setup>
const { data, status, error, refresh, clear } = await useFetch('/api/all-pages');
</script>
  • Individual blog post page — Create a pages/blog/[slug].vue file to display individual blog posts:
pages/blog/[slug].vue
<template>
  <main class="container pb-40 pt-16 mx-auto max-w-[65ch]" v-if="status === 'success' && data">
    <nuxt-link to="/blog" class="text-blue-700 mb-10 inline-block">&larr; Back to blog</nuxt-link>
    <h1 class="text-3xl font-semibold">{{data.title}}</h1>
    <div class="prose">
      <MDC :value="data.content" tag="article" />
    </div>
  </main>
</template>

<script lang="ts" setup>
const route = useRoute()
const slug = route.params.slug

const { data, status, error, refresh, clear } = await useFetch('/api/a-page?slug=' + slug);
</script>
  • Update app.vue — Replace the default content in app.vue with:
app.vue
<template>
  <NuxtPage />
</template>

6. Conclusion

By integrating Stubby CMS with Nuxt.js, you can build dynamic, SEO-friendly applications with powerful content management capabilities. With SSR and ISR support, your content remains fresh and performant while Stubby CMS handles content creation and management. This setup ensures a smooth experience for both developers and users, making it an ideal solution for blogs, documentation sites, and more. Get started today and unlock the full potential of Stubby CMS with Nuxt.js!

 
Source code — Github