Summary: Storyblok is the headless CMS that powers all marketing pages, magazin, podcast pages, redirects, and other non-product content rendered by nuxt-website. Both 7Mind and 7Sleep share a single Storyblok space, accessed via one API token. Sources: direct code inspection (nuxt.config.ts, pages/_dynamicPage/, pages/storyblok.vue, middleware/redirects.js, store/storyblok.js, terraform/web-v2/SECRETS.md) Last updated: 2026-05-15
Role in the system
Storyblok is the source of truth for editorial content on the public web. Most pages that are not part of the auth/checkout/account flow are rendered by fetching a Storyblok story and mapping its body blocks to Vue components.
The Nuxt module @storyblok/nuxt-2 is configured in nuxt-website via:
- Module registration in
buildModulesofnuxt.config.ts - Access token from env var
NUXT_ENV_STORYBLOK_TOKEN cacheProvider: 'memory'
There is one Storyblok space backing both brands. The access token does not encode the brand. Brand-specific filtering happens in client/server code.
What Storyblok owns
- Landing pages (
pages/_dynamicPage/landing/) - Legal pages (
pages/_dynamicPage/legal/) - Catch-all dynamic routes under
_dynamicPage/_subroute/_subsubroute - Magazin articles and previews
- Podcast pages
- Campaign pages (
campaigns/*) - Multiplier and B2B pages (
multiplier-pages/*,b2b-pages/*) - Redirect definitions (Storyblok stories under a
redirects/folder feed into the redirect middleware) - Global components (under
global/)
How content gets rendered
- At build time,
fetchStoryblokPages()innuxt.config.tscallscdn/links(published) to enumerate every story slug. - Filtered slugs become routes in the per-theme sitemap. Stories under
landing-pages,legal-pages,campaigns,podcast,multiplier-pages,b2b-pagesare emitted using theirreal_path; others use a slug-based path. - At request time, the catch-all page in
pages/_dynamicPage/callsuseStoryblokApi().getStory(...)to fetch the story, commits it to thestoryblokVuex module, and renders<component :is="blok.component">for each body block. Component name in Storyblok must match a globally registered Vue component name. - Layout (
defaultvsminimal) and the impact banner visibility are story-controlled via Vuex mutations from inside the page setup. - For preview mode,
pages/storyblok.vueusesversion: 'draft'anduseStoryblokBridgefor live editing.
Filtering and exclusions
The build-time slug list filters out:
- Folders (
is_folder) - Unpublished stories
- Anything starting with
redirects/orglobal/ - When generating French sitemap routes: stories under
multiplier-pages,b2b-pages,podcast,warum-meditation-lernen
Magazin slugs are rewritten (magazin/de → magazin, magazin/en/ → en/magazine/) so the URLs match the configured i18n routing.
What agents should know
- Adding a new Storyblok component requires (a) creating the component in Storyblok with the correct technical name, and (b) registering a Vue component with the same name globally so
<component :is>can resolve it. Most live incomponents/storyblok/. - Changing the Storyblok slug structure can break the sitemap generator. The folder-prefix conventions in
fetchStoryblokPagesare load-bearing. - The access token is the same for staging and production. There is no separate staging Storyblok space; published vs draft is the only environment-like distinction.
- Storyblok is a runtime dependency. If Storyblok is unreachable, dynamic pages will fail to render. The build-time link fetch is wrapped in a try/catch and falls through with no routes if it fails.
- For multi-brand work, the same Storyblok space is used. Brand-specific content uses stories under brand-specific folders or filters in the consumer code; Storyblok does not multi-tenant.
Related
- nuxt-website — the only repo that currently consumes Storyblok
- dual-brand-routing — brand awareness is layered on top of a single Storyblok space
- multi-domain-hostname-rewriting — Storyblok content is brand-aware, not domain-aware; hostname rewriting happens at the API layer below