Almost EVERYONE who tried headless systems said they saw benefits. Download the state of CMS now!

Storyblok now on AWS Marketplace: Read more

O’Reilly Report: Decoupled Applications and Composable Web Architectures - Download Now

Empower your teams & get a 582% ROI: See Storyblok's CMS in action

Skip to main content

How to generate routes for Nuxt with Storyblok?

  • FAQ
  • How to generate routes for Nuxt.js using Storyblok

Nuxt

Nuxt v3 allows you to generate your website statically (SSG) by prerendering all your routes based on your page directory, but since we are fetching the stories from the Storyblok API, we need to tell Nuxt which routes we want to pre-render.

For that, Nuxt uses a new server engine Nitro that provides us a really useful hook nitro:config where we are going to fetch our routes and tell Nitro to prerender them.

We are going to use the fetch API and the Storyblok CDN API v2.

NOTE: To use fetch you need Node v18 or higher.

// nuxt.config.ts
import { fetchStories } from './fetchStories.ts'

export default defineNuxtConfig({
  hooks: {
    async 'nitro:config'(nitroConfig) {
      if (!nitroConfig || nitroConfig.dev) {
        return
      }
      const token = process.env.STORYBLOK_TOKEN

      let cache_version = 0

      // other routes that are not in Storyblok with their slug.
      let routes = ['/'] // adds home directly but with / instead of /home
      try {
        const result = await fetch(`https://api.storyblok.com/v2/cdn/spaces/me?token=${token}`)

        if (!result.ok) {
          throw new Error('Could not fetch Storyblok data')
        }
        // timestamp of latest publish
        const space = await result.json()
        cache_version = space.space.version

        // Recursively fetch all routes and set them to the routes array
        await fetchStories(routes, cache_version)
       // Adds the routes to the prerenderer
        nitroConfig.prerender.routes.push(...routes)
      } catch (error) {
        console.error(error)
      }
    },
  },
})

We move the fetching of the stories to an outside function called fetchStories in case we need pagination (For spaces with +100 Stories, see API limit here)

async function fetchStories(routes: string[], cacheVersion: number, page: number = 1) {
  const token = process.env.STORYBLOK_TOKEN
  const version = 'published'
  const perPage = 100
  const toIgnore = ['home', 'en/settings']

  try {
 const response = await fetch(
      `https://api.storyblok.com/v2/cdn/links?token=${token}&version=${version}&per_page=${perPage}&page=${page}&cv=${cacheVersion}`,
    )
    const data = await response.json()

    // Add routes to the array
    Object.values(data.links).forEach(link => {
      if (!toIgnore.includes(link.slug)) {
        routes.push('/' + link.slug)
      }
    })

    // Check if there are more pages with links

    const total = response.headers.get('total')
    const maxPage = Math.ceil(total / perPage)

    if (maxPage > page) {
      await fetchStories(routes, cacheVersion, ++page)
    }
  } catch (error) {
    console.error(error)
  }
}

Nuxt v2

Nuxt.js allows you to export your application as a statically generated website. To do so it will need to know every route available. You can use one of the following two examples to generate your routes for Nuxt.js using Storyblok.

import axios from 'axios'

export default {
  generate: {
    routes: function (callback) {
      const token = process.env.STORYBLOK_TOKEN
      const version = 'draft'
      const perPage = 2
      let cacheVersion = 0
      const page = 1

      const toIgnore = ['home', 'en/settings']
      
       // other routes that are not in Storyblok with their slug.
      const routes = ['/'] // adds / directly
   
       // Load space and receive latest cache version key to improve performance
      axios.get(`https://api.storyblok.com/v2/cdn/spaces/me?token=${token}`).then((space) => {
   
         // timestamp of latest publish
         cacheVersion = space.data.space.version
   
         // Call for all Links using the Links API: https://www.storyblok.com/docs/Delivery-Api/Links
        axios.get(`https://api.storyblok.com/v2/cdn/links?token=${token}&version=${version}&cv=${cacheVersion}&per_page=${perPage}&page=${page}`).then((res) => {
          Object.keys(res.data.links).forEach((key) => {
            if (!toIgnore.includes(res.data.links[key].slug)) {
              routes.push('/' + res.data.links[key].slug)
            }
          })

          // Check if there are more pages available otherwise execute callback with current routes.
          const total = res.headers.total
          const maxPage = Math.ceil(total / perPage)
          if (maxPage <= 1) {
            callback(null, routes)
            return;
          }

          // Since we know the total we now can pregenerate all requests we need to get all stories
        let contentRequests = []
        for (let page = 2; page <= maxPage; page++) {
          contentRequests.push(axios.get(`https://api.storyblok.com/v1/cdn/links?token=${token}&version=${version}&per_page=${per_page}&page=${page}`))
        }
   
        // Axios allows us to exectue all requests using axios.spread we will than generate our routes and execute the callback
        axios.all(contentRequests).then(axios.spread((...responses) => {
          responses.forEach((response) => {
            Object.keys(response.data.links).forEach((key) => {
              if (!toIgnore.includes(res.data.links[key].slug)) {
                routes.push('/' + res.data.links[key].slug)
              }
            })
          })
 
          callback(null, routes)
        })).catch(callback)
        })
      }) 
    }
  },
}