docs
  1. Integrations
  2. Cms
  3. Storyblok

Storyblok

Overview

Storyblok is a leading headless CMS known for its powerful Visual Editor and component-based structure. The SCAYLE Storefront Application includes a default, high-quality integration that provides various helpers and a pre-built component library to quickly introduce custom content into your shop.

The integration uses the @storyblok/nuxt module, offering a smooth workflow from content creation to deployment. You get full control over your content with instant visual feedback and a flexible component system.

Key Features

  • Visual Editor: Preview your local development environment directly within the Storyblok Visual Editor for a true What-You-See-Is-What-You-Get experience.
  • Component Flexibility: Use a comprehensive set of pre-built base components or create custom components tailored to your needs.
  • Automatic Rendering: The StoryblokComponent automatically renders the appropriate Vue component based on your content structure.
  • Multi-Language Support: Built-in folder-based internationalization with automatic slug resolution across different routing modes.
  • Type Safety: Full TypeScript support with generated types for your content models.

Getting Started

Prerequisites

Before setting up Storyblok, ensure you have the following:

  • Storyblok account: Sign up at storyblok.com.
  • Storyblok space: Create a new space for your new Storefront Application project.
  • Access tokens:
    • Public/Delivery Token: Used to fetch content during runtime, see Access Token.
    • Personal Access Token: Used to generate local types and import the content model during setup, see Personal Access Token.

Setup Your Storyblok Storefront

The Storyblok integration is included with the Storefront Application. To enable and configure it:

Step 1: Run Setup Command

Run the SCAYLE Storefront CLI setup command, passing storyblok with the --provider flag:

This command prompts you to:

  • Select a CMS provider (choose "storyblok").
  • Provide your Storyblok space ID.
  • Provide an access token.
  • Provide a personal access token.
  • Specify whether to import the content model.

Importing the content model is only intended for empty CMS spaces. Running it on spaces with existing content may cause issues.

Step 2: Review Configuration

After running the setup command, your nuxt.config.ts will include:

The following environment variables are updated in your local .env file:

  • CMS_PROVIDER (required during built-time)
  • NUXT_PUBLIC_CMS_SPACE (required during runtime)
  • NUXT_PUBLIC_CMS_ACCESS_TOKEN (required during runtime)
  • NUXT_PUBLIC_CMS_ALLOW_DRAFTS (optional during runtime)
  • STORYBLOK_PERSONAL_TOKEN (required for cli commands)

Step 3: Environment Configuration & Deployment

The @storyblok/nuxt module uses Nuxt's RuntimeConfig, which provides flexibility for different deployment environments.

Runtime vs Build-Time Configuration

Unlike traditional build-time configuration, environment variables can be set at runtime. This means:

  • During development: Set variables in your .env file.
  • During deployment: Update environment variables on your hosting platform without rebuilding.
  • Different environments: Use the same build for development, staging, and production with different variables.

Environment Variables Reference

All Storyblok configuration settings can be set via environment variables:

Production Deployment Checklist

  • Use Public/Delivery token in production (not Preview token)
  • Optionally set NUXT_PUBLIC_CMS_ALLOW_DRAFTS=false in production
  • Use Preview token only in development/staging environments
  • Ensure all required environment variables are configured on your hosting platform
  • Test content loading in your production environment before going live

Architecture & Integration

Storyblok uses a component-based architecture where content is built from reusable blocks called "components" or "blocks". Each Storyblok component maps to a Vue component in your application, enabling a seamless bridge between your CMS and the Storefront Application.

Component Mapping Pattern

  • Storyblok component Text → Vue component TextComponent.vue
  • Storyblok component Image → Vue component ImageComponent.vue
  • Storyblok component Section → Vue component SectionComponent.vue
  • Storyblok component ProductListingPage → Vue component ProductListingPageComponent.vue

Content Organization Architecture

The Storyblok integration uses a folder-based approach for organizing content across multiple languages and shops. Understanding the folder structure and naming conventions is essential for proper routing and content resolution.

Folder Naming Conventions

See #create-locale-folders for a folder structure example.

  • Use shop codes: Folder names must match the code field in your shop configuration (e.g., de, en, ch).
    • Use the shop code, NOT the full locale (e.g., de, not de-DE).
    • Shop code is used for URL routing (e.g., de, at, de-en).
    • Full locale is the BCP-47 code (e.g., de-DE, de-AT, en-DE).
  • Homepage: Each locale folder must contain a homepage story for the root path (/).
  • Categories folder: Create a folder with slug c inside each locale folder for Product Listing Page (PLP) CMS content.
    • Category pages use the pattern c-{categoryId} (e.g., c-91825 for category ID 91825).
    • The application automatically constructs the slug as c/c-{categoryId} when fetching category content.
  • Content folder: Create a folder with slug content inside each locale folder for general CMS pages (except homepage).
    • All content pages (about, privacy, etc.) go inside this folder.
    • The application automatically prepends content/ to the page slug when fetching.
  • Consistent structure: Maintain the same folder structure across all locales for easier management.
  • Path mirroring: The path structure inside each locale folder mirrors your application's URL structure.

Routing Mode Integration

The Storyblok integration automatically handles slug resolution based on your Storefront Application's routing configuration. The routing mode is configured via the SHOP_SELECTOR_MODE environment variable.

All content uses a folder-based approach with locale prefixes, regardless of which routing mode you use. This provides consistency and simplifies content management.

Path-Based Routing

Configuration: SHOP_SELECTOR_MODE=path

In path-based routing, each shop has its own URL prefix:

URLStoryblok Slug
/de/de/homepage
/de/content/aboutde/content/about
/de/content/privacyde/content/privacy
/de/c/category-slug-91825 (Category)de/c/c-91825
/en/content/abouten/content/about
/ch/c/category-slug-139 (Category)ch/c/c-139

The integration automatically removes the shop prefix from the URL path before constructing the Storyblok slug.

Domain-Based Routing

Configuration: SHOP_SELECTOR_MODE=domain

In domain-based routing, each shop uses a different domain or subdomain:

URLStoryblok Slug
de.example.com/de/homepage
de.example.com/content/aboutde/content/about
de.example.com/content/privacyde/content/privacy
de.example.com/c/category-slug-91825 (Category)de/c/c-91825
en.example.com/content/abouten/content/about
ch.example.com/c/category-slug-139 (Category)ch/c/c-139

The integration determines the locale from the domain and prepends it to the path.

Path-Except-Default Routing

Configuration: SHOP_SELECTOR_MODE=path_or_default

In path-except-default routing, the default shop has no URL prefix, while other shops do:

Assuming de is the default shop:

URLStoryblok Slug
/ (default locale)de/homepage
/content/about (default locale)de/content/about
/content/privacy (default locale)de/content/privacy
/c/category-slug-91825 (Category, default)de/c/c-91825
/en/content/abouten/content/about
/en/en/homepage
/ch/c/category-slug-139 (Category)ch/c/c-139

The integration automatically detects the current locale and constructs the appropriate Storyblok slug.

Multiple Shops with Same Language

You can have multiple shops sharing the same language but with different content:

Example: German shops for different countries:

  • Shop 1 (Germany): locale: 'de-DE', code: 'de' → Storyblok folder de/,
  • Shop 2 (Austria): locale: 'de-AT', code: 'at' → Storyblok folder at/,
  • Shop 3 (Switzerland): locale: 'de-CH', code: 'ch' → Storyblok folder ch/.

Each shop gets its own Storyblok folder based on its unique code, allowing completely different content even when using the same language. This is useful for:

  • Country-specific campaigns and promotions,
  • Different product catalogs per country,
  • Region-specific legal content,
  • Localized imagery and messaging.

Sharing Content Across Shops

If you want multiple shops to use the same content from a single Storyblok folder, use the folderMapping configuration to map different shop codes to the same folder:

With this configuration:

  • All three shops (de, at, ch) fetch content from the same de/ folder in Storyblok.
  • You only need to maintain one set of content for all German-speaking markets.
  • Reduces content duplication and maintenance effort.
  • Perfect for markets with minimal regional differences.

Use cases for shared content:

  • Testing new markets before creating localized content.
  • Small markets that don't require region-specific content.
  • Consistent brand messaging across similar regions.
  • Reducing initial content creation effort.

Content Fetching Flow

When a user visits your Storefront Application, the integration follows this flow:

  1. Route resolves: User navigates to a URL (e.g., /en/content/about).
  2. Slug resolution: The integration resolves the route to a Storyblok slug (e.g., en/content/about).
  3. API request: Content is fetched from the Storyblok API.
  4. Component mapping: Storyblok components are mapped to corresponding Vue components.
  5. Rendering: Vue components render the content with proper styling and interactivity.

Visual Editor Integration

The Storyblok Visual Editor enables you to see changes in real-time as you edit content in Storyblok:

  1. Editor detection: The integration detects the _storyblok query parameter in the URL.
  2. Draft content: Draft versions are loaded instead of published versions.
  3. Live updates: Changes in Storyblok appear instantly in the preview browser.
  4. Deep reactivity: Component updates trigger Vue re-renders automatically.
Storyblok Preview

Storyblok Preview

Configure Preview Access

To display unpublished changes on initial load in the Storyblok editor, your access token must have the Preview access level. If your token has a different access level, unpublished changes appear only after modifications are made in the editor.

Ensure each component applies the v-editable directive to its root element for full Visual Editor functionality.

Configure the Visual Editor Base URL

To set up the Visual Editor, configure the base URL under the Space "Settings" page in Storyblok:

  1. Go to your Storyblok space "Settings".
  2. Find the preview configuration section.
  3. Enter your development environment URL (e.g., http://localhost:3000 for local development).
  4. For shops using path prefixes, include them in the URL.
  5. For shops with separate domains, enter the full domain.

Storyblok automatically appends the page slug to this URL, ensuring the correct page displays in the editor.

Enable Draft Content

Set the following environment variable to display draft content:

Visual Editor Support

Use the v-editable directive to enable click-to-edit functionality in the Storyblok Visual Editor:

The v-editable directive tells Storyblok which elements are editable, enabling content editors to click directly on content in the preview.

Test Live Preview

  1. Open a story in Storyblok.
  2. Click the "Open in Visual Editor" button.
  3. Verify the preview loads correctly.
  4. Make changes in Storyblok and verify they appear instantly.

Embedding CMS Content in Existing Pages

You can enrich existing Storefront Application pages with CMS-managed sections without converting the entire page to CMS. For example, the Product Listing Page (PLP) demonstrates this pattern:

This pattern lets you drop CMS-controlled blocks into any page region while keeping core page logic in your application.


Available Components

The integration provides pre-built components organized by function. These components serve as building blocks for your content. You can explore all components in our UI Component Overview.

Core Content Primitives

ComponentDescription
TextComponentText content with flexible heading levels (h1-h4) and paragraphs.
LinkComponentNavigable text and image links with proper URL resolution.
ImageComponentOptimized images with responsive sizing and aspect ratio.
VideoComponentEmbed videos with customizable playback controls.

Interactive & Basic Content Blocks

ComponentDescription
AccordionComponentCollapsible accordion sections.
AccordionItemComponentIndividual accordion items with expand/collapse functionality.
ButtonComponentAction buttons with configurable styling and link destinations.

Layout & Structural

ComponentDescription
PageComponentTop-level page container for full-page content.
ProductListingPageComponentSpecialized component for category page content.
SectionComponentContent container with background image/color support.
GridComponentMulti-column layout system for content organization.
DividerComponentVisual spacing and divider elements.

Rich Content

ComponentDescription
RichTextComponentStoryblok rich text with proper formatting and link resolution.

E-Commerce Specific

ComponentDescription
SliderComponentCarousel for multiple content items.
ProductSliderComponentProduct carousel with selectable products.
RecentlyViewedProductsComponentRecently viewed products.
SmartSortingProductsSliderComponentConfigurable product slider that accepts a Smart Sorting Key and additional parameters.

Guidelines: Content

Create Locale Folders

Organize your content in Storyblok using folders that match your shop codes:

  • Create a folder for each language or shop (e.g., de/, en/, ch/)
  • Inside each language folder, create three subfolders:
    • homepage - Your homepage story,
    • c/ - Category page content (named as c-{categoryId}),
    • content/ - General content pages (about, privacy, terms, etc.).
  • Place your stories in the appropriate folder.
  • Maintain the same structure across all language folders for consistency.

Using Base Components

The included pre-built components provide all the building blocks needed to create common e-commerce content patterns. Compose flexible sections that adapt to your brand and merchandising needs.

Composing E-Commerce Blocks

Hero Banner

Combine these components to create an impactful hero banner:

  1. Use SectionComponent with a background image.
  2. Add TextComponent for the headline and description.
  3. Include one or more ButtonComponent instances for calls-to-action.
  4. Use DividerComponent for vertical spacing.

Structure

Category Teasers / Cards

Create a card grid layout:

  1. Use GridComponent with four columns on desktop, two on mobile.
  2. Inside each column, add SectionComponent with a background image.
  3. Include ButtonComponent to link to the category page.

Structure

Promotional Slider

Implement a carousel pattern:

  1. Use SliderComponent as the container.
  2. For each slide, use SectionComponent as a container.
  3. Add TextComponent for each slide title.
  4. Add DividerComponent as a separator.
  5. Include ButtonComponent to drive users to listings or campaigns.

Structure

Split Image & Text Section

Create a side-by-side layout:

  1. Use GridComponent with two columns on desktop, one on mobile.
  2. First column: SectionComponent with a background image.
  3. Second column: TextComponent for headline/description and ButtonComponent for CTA.

Structure

Creating Content for PLP

The Product Listing Page (PLP) is a main discovery point in the user journey. Our specialized component allows you to seamlessly inject custom, commerce-relevant content before and after your product stream.

Follow these steps to set up content for a specific category:

Step 1: Create the PLP Content Entry

  1. In your Storyblok, navigate to the Categories folder.
  2. Click Create new contentStory.
  3. Define the Name for your content.
  4. Crucially, set the Slug to match the relative URL path of your target category (e.g., c-50341).
  5. In the Content Type field, choose Product Listing Page Component.

Step 2: Configure the PLP Component in Preview

  1. Enter Preview Mode. You will see your category page rendered with two configurable areas on the right side:
    1. Teaser: Content displayed above the product stream (e.g., hero banners).
    2. SEO Content: Content displayed below the product stream.
  2. Let's start by clicking into the Teaser property.

Step 3: Design the Header Teaser (Background Images)

  1. For the Teaser, let's choose a SectionComponent.
  2. Add Background Images: Set the background images for both desktop and mobile. We strongly recommend using a different, smaller image size for mobile to optimize page layout and performance.

Step 4: Design the Header Teaser (Dimensions)

  1. Define Dimensions: Set the component height: e.g., 300px for desktop and 200px for mobile.
  2. You can keep the default vertical and horizontal alignment as center and middle.

Step 5: Design the Header Teaser (Content)

  1. Add Content: Inside the section, add a TextComponent with the type set to Heading 1 and define your desired value (e.g., "New Arrivals – Discover Now").

Step 6: Create the SEO Content

  1. Click into the SEO Content property.
  2. Choose a Rich Text Component. This component allows you to format text and easily highlight key terms, which is crucial for SEO value.

Congratulations! Your customized PLP content is now ready to go live.

Guidelines: Development

Customizing Existing Components

You can customize existing components by modifying their Vue implementations to match your design system and requirements.

How Component Customization Works

All Storyblok components are rendered through the StoryblokComponent registry. To customize a component:

  1. Locate the component in modules/cms/providers/storyblok/components/ .
  2. Modify the Vue component to match your design system.
  3. Test in Live Preview to verify your changes.

Available Components to Customize

The integration provides a comprehensive set of pre-built components that can be customized:

  • TextComponent.vue - Customize text rendering and styling
  • ImageComponent.vue - Customize image display and responsiveness
  • ButtonComponent.vue - Customize button appearance and interactions
  • SectionComponent.vue - Customize layout and spacing
  • GridComponent.vue - Customize grid columns and gaps
  • LinkComponent.vue - Customize link styling and behavior
  • ... and all other available components.

Each component is registered in the StoryblokComponent (see the "Nested Components" section for details on how this registry works).

To customize any component, navigate to modules/cms/providers/storyblok/components/ and modify the Vue implementation to match your brand and design requirements.

Nested Components

Storyblok content types can reference other content types, creating nested component hierarchies. The ContentfulComponent serves as the central registry that maps all Contentful content types to their corresponding Vue components.

How StoryblokComponent Works:

The StoryblokComponent is a dynamic component dispatcher that:

  1. Receives a Storyblok content element (blok) as a prop
  2. Examines the element's component type
  3. Renders the appropriate Vue component based on the component type
  4. Passes the element data to the mapped component

This allows you to build deeply nested content structures where any content type can contain references to other content types, and they'll all render correctly without needing to know about each other.

Example - Nested Content:

Components can contain other components via reference fields:

The StoryblokComponent Registry:

Under the hood, StoryblokComponent.vue contains a series of conditional branches that determine which Vue component to render:

Benefits of This Pattern:

  • Flexibility: Build complex content hierarchies with any combination of bloks.
  • Reusability: Components don't need to know about each other, just about StoryblokComponent .
  • Maintainability: Add new components to one central place (the registry).
  • Visual Editor Support: The v-editable directive enables click-to-edit for all nested components.
  • Fallback Handling: Unknown component types display a helpful message instead of breaking.

Building New Components

Creating new Storyblok components allows you to extend the pre-built library with custom functionality.

Step 1: Define in Storyblok

  1. Go to your Storyblok space → "Components".
  2. Click "Create new component".
  3. Define the component structure with appropriate fields (text, images, references, etc.).
  4. Publish the component schema.

Step 2: Add TypeScript Types

After defining your component in Storyblok, types are automatically generated by running the cms:sync command from your Storefront Application root directory:

This command:

  1. Logs into Storyblok using your STORYBLOK_PERSONAL_TOKEN .
  2. Pulls components from your Storyblok space.
  3. Generates TypeScript types automatically from your component schema.
  4. Organizes types in modules/cms/providers/storyblok/types/gen/ .

What Gets Generated

The cms:sync command generates:

  • Component interfaces: Full TypeScript definitions for all Storyblok components.
  • Type files: Organized in modules/cms/providers/storyblok/types/gen/components/ .
  • Type guards: Ready-to-use type checking utilities (e.g., isTypeMyBannerComponent).

Generated Type Example

After running pnpm cms:sync, your component types are automatically available:

Import Generated Types

Use the auto-generated types in your Vue components:

Never manually edit files in modules/cms/providers/storyblok/types/gen/. These are auto-generated and will be overwritten when you run pnpm cms:sync again.

Step 2: Create Vue Component

Create a corresponding Vue component in your application. Components receive a contentElement prop containing the Storyblok data:

Step 4: Register Component

Register the component in StoryblokComponent.vue to make it renderable:

Best Practices

Content Organization

  • Mirror URL structure: Keep the same folder structure in all locale folders.
  • Consistent naming: Use the same story slugs across locales when possible.
  • Shared assets: Store locale-independent assets (images, videos) separately and reference them.

Translation Workflow

  • Create default locale first: Start with one locale as the source of truth.
  • Use translation workflows: Leverage Storyblok's translation features.
  • Review process: Implement a review process for translated content.

Performance

  • Use caching: The Storefront Application caches Storyblok responses.
  • Lazy loading: Use lazy: true for below-the-fold content.
  • Asset optimization: Use Storyblok's image service for optimized delivery.

SEO Considerations

  • Unique meta tags: Ensure each locale has unique meta titles and descriptions.
  • Hreflang tags: Implement hreflang tags for multilingual SEO.
  • Canonical URLs: Set canonical URLs appropriately for each locale.
  • Sitemap: Generate locale-specific sitemaps.

Development Workflow

  • Use Live Preview: Enable Live Preview for content editors during development.
  • Sync types regularly: Run pnpm cms:sync after content model changes.
  • Test with draft content: Verify draft content displays correctly before publishing.
  • Handle errors gracefully: Implement proper error boundaries and fallbacks.
  • Test across routing modes: Verify content fetching works correctly with your specific routing mode (path-based, domain-based, or path-except-default)

Troubleshooting

Content Not Found (404)

Problem

Content exists in Storyblok but shows 404 in the application.

Solutions

  1. Ensure content is in the correct locale folder.
  2. Confirm the locale code matches your i18n configuration.
  3. Verify the story slug matches the URL path.
  4. If using custom folderMapping, ensure it's correct.

Debugging

Preview Not Working

Problem

Visual Editor preview doesn't load or shows errors.

Solutions

  1. Verify the preview URL matches your routing mode.
  2. Ensure you're using a preview access token.
  3. Set NUXT_PUBLIC_CMS_ALLOW_DRAFTS=true for preview.
  4. Verify CORS is configured correctly in your application.

Debugging

  • Open browser DevTools and check for network errors.
  • Verify the _storyblok query parameter is present in the URL.
  • Check the console for warning messages.

Wrong Content Loaded

Problem

Content from wrong locale is displayed.

Solutions

  1. Verify i18n.locale.value is correct.
  2. Ensure your routing mode is configured correctly.
  3. If page cache is used, clear page cache if content is cached incorrectly.

Debugging

Slug Mismatch

Problem

Slug resolution doesn't match the expected Storyblok path.

Solutions

  1. Verify the shop prefix is being removed correctly.
  2. If using folderMapping, ensure configuration is correct.
  3. Use the resolveStoryblokSlug utility directly to test.

Debugging