npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

astro-loader-storyblok

v1.0.16

Published

Astro content loader for Storyblok

Downloads

84

Readme

astro-loader-storyblok

types CI Build Version Dependencies Maturity License

A robust Astro content loader for Storyblok.

astro-loader-storyblok is a community-driven continuation of Storyblok’s archived Astro Content Layer integration, enabling smooth integration between Storyblok CMS and Astro content collections. Read more about the origins of this project here.

Features

  • Full Astro Content Layer API support - Compatible with Astro 5.0+
  • 🗂️ Stories and datasources - Comprehensive support for both Storyblok stories and datasources
  • 🚀 Optimized performance - Incremental updates and efficient caching
  • Automatic schema generation - Auto-generates Astro collection schemas for datasources
  • 🎯 Content type filtering - Load specific content types or all stories
  • 📊 Flexible sorting - Multiple sorting options for your content
  • 📦 TypeScript ready - Full TypeScript support with type definitions

Performance Features

  • Cache Version Optimization: Uses Storyblok's cache version (cv) to detect when content has changed, avoiding unnecessary API calls
  • Incremental Updates: Only fetches content that has changed since the last published date
  • Efficient Caching: Stores metadata about cache version and last published date to minimize API calls
  • Selective Loading: Load only specific content types to reduce payload size

Installation

npm install astro-loader-storyblok
# or
pnpm add astro-loader-storyblok
# or
yarn add astro-loader-storyblok

Quick Start

💡 Want to jump right in? Check out the playground example to see a working implementation before setting up your own project.

1. Configure your Astro content collection

Create or update your src/content/config.ts:

import { defineCollection } from "astro:content";
import { StoryblokLoaderStories } from "astro-loader-storyblok";

const stories = defineCollection({
  loader: StoryblokLoaderStories({
    accessToken: "your-storyblok-access-token",
  }),
});

export const collections = { stories };

2. Use in your Astro pages

---
// src/pages/blog/[...slug].astro
import { getCollection, getEntry } from "astro:content";

export async function getStaticPaths() {
  const stories = await getCollection("stories");
  return stories.map((story) => ({
    params: { slug: story.data.full_slug },
    props: { story },
  }));
}

const { story } = Astro.props;
---

<html>
  <head>
    <title>{story.data.name}</title>
  </head>
  <body>
    <h1>{story.data.content.title}</h1>
    <div set:html={story.data.content.body} />
  </body>
</html>

Advanced Usage (New in v0.2.0)

For more advanced use cases, you can use the new StoryblokLoader class which provides better performance through shared cache version management across multiple collections:

import { defineCollection } from "astro:content";
import { StoryblokLoader } from "astro-loader-storyblok";

// Create a shared loader instance
const storyblokLoader = new StoryblokLoader({
  accessToken: "your-storyblok-access-token",
});

// Define multiple collections that share the same cache version
const stories = defineCollection({
  loader: storyblokLoader.getStoriesLoader({
    contentTypes: ["article", "page"],
    storyblokParams: {
      version: "published",
      sort_by: "created_at:desc",
    },
  }),
});

const categories = defineCollection({
  loader: storyblokLoader.getDatasourceLoader({
    datasource: "categories",
  }),
});

export const collections = { stories, categories };

Benefits of the StoryblokLoader Class

  • Shared Cache Management: Multiple collections share the same cache version, reducing redundant API calls and preventing conflicts if changes are made to a Storyblok space while collections are being loaded by Astro.
  • Better Performance: Cache version is fetched once and reused across all loaders
  • Cleaner Architecture: Centralized configuration and better separation of concerns

Stories Loader

The StoryblokLoaderStories allows you to load stories from Storyblok into your Astro content collections.

Basic Configuration

import { defineCollection } from "astro:content";
import { StoryblokLoaderStories } from "astro-loader-storyblok";

const stories = defineCollection({
  loader: StoryblokLoaderStories({
    accessToken: "your-access-token",
  }),
});

Advanced Configuration

import { StoryblokLoaderStories, SortByEnum, type StorySortFunction } from "astro-loader-storyblok";

const stories = defineCollection({
  loader: StoryblokLoaderStories({
    accessToken: "your-access-token",

    // Filter by specific content types
    contentTypes: ["article", "page", "product"],

    // Use UUIDs instead of slugs as IDs
    useUuids: true,

    // Sort stories using the new sortBy property (takes precedence over storyblokParams.sort_by)
    sortBy: SortByEnum.CREATED_AT_DESC,

    // Or use a custom sort function (takes precedence over sortBy)
    customSort: (a, b) => {
      // Example: Sort by a custom priority field
      const priorityA = a.content?.priority || 0;
      const priorityB = b.content?.priority || 0;
      return priorityB - priorityA; // Higher priority first
    },

    // Additional Storyblok API options
    apiOptions: {
      region: "us", // 'eu' (default), 'us', 'ap', 'ca', 'cn'
      https: true,
      cache: {
        type: "memory",
      },
    },

    // Storyblok API parameters
    storyblokParams: {
      // Content version
      version: "draft", // "draft" or "published" (default)

      // Exclude specific slugs
      excluding_slugs: "home,about,contact",

      // Sort stories (lower precedence than sortBy/customSort)
      sort_by: SortByEnum.CREATED_AT_DESC,
    },
  }),
});

Datasource Loader

The StoryblokLoaderDatasource allows you to load data from Storyblok datasources into your Astro content collections. Datasources in Storyblok are useful for managing structured data like categories, tags, or any other reference data.

Basic Usage

import { defineCollection } from "astro:content";
import { StoryblokLoaderDatasource } from "astro-loader-storyblok";

const categories = defineCollection({
  loader: StoryblokLoaderDatasource({
    accessToken: "your-storyblok-access-token",
    datasource: "categories", // Your datasource slug in Storyblok
  }),
});

export const collections = { categories };

Datasource Configuration

const categories = defineCollection({
  loader: StoryblokLoaderDatasource({
    accessToken: "your-access-token",
    datasource: "categories",     // Datasource slug in Storyblok

    // Optionals:
    switchNamesAndValues: true,   // Use value as ID and name as body
    dimension: "es",              // Specify query dimension
    apiOptions: { region: "us" }, // Additional Storyblok API options
  }),
});

Using Datasource Data

---
// src/pages/categories.astro
import { getCollection } from "astro:content";

const categories = await getCollection("categories");
---

<html>
  <body>
    <h1>Categories</h1>
    <ul>
      {categories.map((category) => (
        <li key={category.id}>
          <strong>{category.id}</strong>: {category.body}
        </li>
      ))}
    </ul>
  </body>
</html>

Data Structure

By default, the loader uses the datasource entry's name as the collection entry ID and the value as the body content. You can switch this behavior using the switchNamesAndValues option.

API Reference

StoryblokLoaderStories Configuration

The StoryblokLoaderStories function accepts a single configuration object that combines both loader-specific configuration and Storyblok API parameters:

export interface StoryblokLoaderStoriesConfig {
  accessToken: string;
  apiOptions?: ISbConfig;
  contentTypes?: string[];
  useUuids?: boolean;
  sortBy?: string;
  customSort?: StorySortFunction;
  storyblokParams?: ISbStoriesParams;
}

| Option | Type | Default | Description | |--------------------|----------------------|--------------|----------------------------------------------| | accessToken | string | Required | Your Storyblok access token | | apiOptions | ISbConfig | {} | Additional Storyblok API configuration | | contentTypes | string[] | undefined | Array of content types to load | | useUuids | boolean | false | Use story UUIDs instead of slugs as IDs | | sortBy | string | undefined | Sort order for stories (overrides storyblokParams.sort_by) | | customSort | StorySortFunction | undefined | Custom sort function (overrides sortBy) | | storyblokParams | ISbStoriesParams | {} | Storyblok API query parameters (see below) |

Storyblok API Parameters (storyblokParams):

The storyblokParams property accepts all standard Storyblok Stories API parameters:

| Option | Type | Default | Description | |-------------------|--------------------------|---------------|------------------------------------------| | version | "draft" \| "published" | "published" | Content version to load | | excluding_slugs | string | undefined | Comma-separated list of slugs to exclude | | sort_by | string | undefined | Sort order for stories | | starts_with | string | undefined | Filter by slug prefix | | by_slugs | string | undefined | Filter by specific slugs |

For a complete list of available parameters, see the Storyblok Stories API documentation.

Sorting Priority:

The loader supports multiple ways to sort stories, with the following precedence order (highest to lowest):

  1. customSort - Custom sort function for complex sorting logic
  2. sortBy - Simple string-based sorting parameter
  3. storyblokParams.sort_by - Legacy Storyblok API parameter

When multiple sorting options are provided, only the highest priority option will be used.

StoryblokLoaderDatasource Configuration

export interface StoryblokLoaderDatasourceConfig {
  accessToken: string;
  datasource: string;
  dimension?: string;
  switchNamesAndValues?: boolean;
  apiOptions?: ISbConfig;
}

| Option | Type | Default | Description | |------------------------|-------------|--------------|----------------------------------------------------------| | accessToken | string | Required | Your Storyblok access token | | datasource | string | Required | The slug of your Storyblok datasource | | dimension | string | undefined | Filter entries by dimension (if configured in Storyblok) | | switchNamesAndValues | boolean | false | Use value as ID and name as body instead of the default | | apiOptions | ISbConfig | {} | Additional Storyblok API configuration |

StorySortFunction Type

For advanced sorting scenarios, you can provide a custom sort function:

export type StorySortFunction = (a: ISbStoryData, b: ISbStoryData) => number;

The function should return:

  • A negative number if the first story should come before the second
  • A positive number if it should come after
  • Zero if they are equal

Sorting Options

The SortByEnum enum provides the following default sorting options for use in the sort_by parameter:

import { SortByEnum } from "astro-loader-storyblok";

// Available sorting options
SortByEnum.CREATED_AT_ASC             // "created_at:asc"
SortByEnum.CREATED_AT_DESC            // "created_at:desc"
SortByEnum.FIRST_PUBLISHED_AT_ASC     // "first_published_at:asc"
SortByEnum.FIRST_PUBLISHED_AT_DESC    // "first_published_at:desc"
SortByEnum.NAME_ASC                   // "name:asc"
SortByEnum.NAME_DESC                  // "name:desc"
SortByEnum.SLUG_ASC                   // "slug:asc"
SortByEnum.SLUG_DESC                  // "slug:desc"
SortByEnum.UPDATED_AT_ASC             // "updated_at:asc"
SortByEnum.UPDATED_AT_DESC            // "updated_at:desc"

// Usage example
const stories = defineCollection({
  loader: StoryblokLoaderStories(
    { accessToken: "your-token" },
    {
      version: "published",
      sort_by: SortByEnum.CREATED_AT_DESC,
    }
  ),
});

You may also specify a custom string for custom sorting options. For more details, refer to the Storyblok Stories API documentation.

Examples

📁 Live Example: See the playground/minimal-astro-project for a complete working example that you can run locally.

Loading Blog Posts

// src/content/config.ts
const blog = defineCollection({
  loader: StoryblokLoaderStories({
    accessToken: import.meta.env.STORYBLOK_TOKEN,
    contentTypes: ["blog-post"],
    storyblokParams: {
      version: "published",
      sort_by: SortByEnum.CREATED_AT_DESC,
    },
  }),
});

Multi-region Setup

const stories = defineCollection({
  loader: StoryblokLoaderStories({
    accessToken: import.meta.env.STORYBLOK_TOKEN,
    apiOptions: {
      region: "us", // for US region
    },
    storyblokParams: {
      version: "published",
    },
  }),
});

Development vs Production

const stories = defineCollection({
  loader: StoryblokLoaderStories({
    accessToken: import.meta.env.STORYBLOK_TOKEN,
    storyblokParams: {
      version: import.meta.env.DEV ? "draft" : "published",
    },
  }),
});

Combined Stories and Datasources

import { defineCollection } from "astro:content";
import { StoryblokLoaderStories, StoryblokLoaderDatasource } from "astro-loader-storyblok";

const stories = defineCollection({
  loader: StoryblokLoaderStories({
    accessToken: import.meta.env.STORYBLOK_TOKEN,
    contentTypes: ["article", "page"],
    storyblokParams: {
      version: "published",
    },
  }),
});

const categories = defineCollection({
  loader: StoryblokLoaderDatasource({
    accessToken: import.meta.env.STORYBLOK_TOKEN,
    datasource: "categories",
  }),
});

export const collections = { stories, categories };

Custom Sorting Examples

Priority-based Sorting

Sort stories by a custom priority field in your content:

const stories = defineCollection({
  loader: StoryblokLoaderStories({
    accessToken: import.meta.env.STORYBLOK_TOKEN,
    contentTypes: ["article"],
    customSort: (a, b) => {
      const priorityA = a.content?.priority || 0;
      const priorityB = b.content?.priority || 0;
      return priorityB - priorityA; // Higher priority first
    },
  }),
});

Multi-level Sorting

First sort by category, then by date within each category:

const stories = defineCollection({
  loader: StoryblokLoaderStories({
    accessToken: import.meta.env.STORYBLOK_TOKEN,
    customSort: (a, b) => {
      // First level: sort by category
      const categoryA = a.content?.category || "zzz";
      const categoryB = b.content?.category || "zzz";
      
      if (categoryA !== categoryB) {
        return categoryA.localeCompare(categoryB);
      }
      
      // Second level: sort by date (newest first within same category)
      const dateA = new Date(a.created_at || 0);
      const dateB = new Date(b.created_at || 0);
      return dateB.getTime() - dateA.getTime();
    },
  }),
});

Featured Content First

Show featured content at the top, then sort the rest by date:

const stories = defineCollection({
  loader: StoryblokLoaderStories({
    accessToken: import.meta.env.STORYBLOK_TOKEN,
    customSort: (a, b) => {
      // Featured content first
      const featuredA = a.content?.featured || false;
      const featuredB = b.content?.featured || false;
      
      if (featuredA !== featuredB) {
        return featuredB ? 1 : -1; // Featured items come first
      }
      
      // Then sort by creation date (newest first)
      const dateA = new Date(a.created_at || 0);
      const dateB = new Date(b.created_at || 0);
      return dateB.getTime() - dateA.getTime();
    },
  }),
});

What's New

v1.0.0

Major Release 🎉🎉🎉

This is the first stable release of astro-loader-storyblok! After some testing and refinement, the loader is now "feature-complete".

⚡ Performance Improvements

  • Optimized cache version checking: Implemented promise-based cache version updates to prevent redundant API calls when multiple collections are loading simultaneously
  • Better collection synchronization: Fixed issues where pressing 's' to sync collections in development mode wouldn't properly trigger refetching (#4)

🔧 Enhanced Developer Experience

  • Better error context: More detailed error reporting with collection-specific context
  • Improved debugging: Enhanced debug logging output with better formatting and more informative messages
  • Playground: Includes a playground sample useful for testing this loader.

� New Sorting Features

  • Custom Sort Functions: New customSort property allows complex sorting logic using custom functions
  • Simplified Sorting: New sortBy property provides easier sorting configuration with priority over storyblokParams.sort_by
  • Sorting Precedence: Clear hierarchy of sorting options: customSort > sortBy > storyblokParams.sort_by
  • Maintained Sort Order: Improved sorting logic ensures proper order when adding new entries to cached collections

�🏗️ Code Quality & Maintenance

  • Comprehensive test coverage: Expanded test suite with edge cases, integration tests, and sorting functionality across multiple content types
  • Code organization: Removed redundant code and cleaned up internal implementations
  • Documentation updates: Refreshed examples and removed references to deprecated functionality

Breaking Changes

  • Removed deprecated configuration: The second parameter pattern StoryblokLoaderStories(config, storyblokParams) has been completely removed. Use the single configuration object with storyblokParams property instead.

🚀 Migration from v0.x

Old (no longer supported):

const stories = defineCollection({
  loader: StoryblokLoaderStories(config, { version: "draft" }),
});

New (v1.0.0+):

const stories = defineCollection({
  loader: StoryblokLoaderStories({
    ...config,
    storyblokParams: { version: "draft" },
  }),
});

v0.2.7

  • Changed the name appearing in Astro's logger (removed 'astro-' for consistency with other Astro integrations).

v0.2.4

  • Add FIRST_PUBLISHED_AT_ASC, FIRST_PUBLISHED_AT_DESC, PUBLISHED_AT_ASC, PUBLISHED_AT_DESC to SortByEnum.

v0.2.3

  • Fix: schema for Datasource

v0.2.2

  • Fix: proper overload for StoryblokLoaderStories()

v0.2.1

  • Add test suite
  • Add Github workflows

v0.2.0

🚀 Performance Improvements

  • Enhanced Cache Version System: Now uses Storyblok's cache version (cv) to detect content changes more efficiently, reducing unnecessary API calls
  • Smart Cache Validation: Automatically skips fetching when no changes are detected in your Storyblok space
  • Shared Cache Management: New StoryblokLoader class enables multiple collections to share the same cache version

🏗️ Architecture Improvements

  • Improved Code Organization: Split the monolithic loader into separate, focused modules:
    • StoryblokLoaderStories - Stories functionality
    • StoryblokLoaderDatasource - Datasource functionality
    • StoryblokLoader - Advanced class-based usage
  • Better Type Safety: Enhanced TypeScript definitions and schema validation for datasources

🛠️ Developer Experience

  • Better Debugging: Enhanced logging with collection context and debug information
  • Improved Error Messages: More detailed error reporting with better context
  • Migration Warnings: Clear deprecation warnings with migration guidance

Breaking Changes

Since v0.2.0

This section documents changes that may affect your configuration ~~but are backward compatible through deprecation warnings~~.

⚠️ Configuration Structure Change (Deprecated)

What changed: The two-parameter configuration pattern is deprecated in favor of a single configuration object.

Impact: ~~Your existing code will continue to work but will show deprecation warnings.~~ < v0.2.0 configuration pattern will not work anymore.

// ⚠️ DEPRECATED: ABANDONED since v1.0.0
const stories = defineCollection({
  loader: StoryblokLoaderStories(config, { version: "draft" }),
});

// ✅ RECOMMENDED
const stories = defineCollection({
  loader: StoryblokLoaderStories({
    ...config,
    storyblokParams: { version: "draft" },
  }),
});

🚀 New Advanced Usage Pattern

What's new: Introduction of the StoryblokLoader class for better performance in multi-collection setups.

// ✅ NEW: Advanced usage with shared cache management
import { StoryblokLoader } from "astro-loader-storyblok";

const storyblokLoader = new StoryblokLoader({ accessToken: "token" });

const stories = defineCollection({
  loader: storyblokLoader.getStoriesLoader({
    contentTypes: ["article"],
    storyblokParams: { version: "published" },
  }),
});

const categories = defineCollection({
  loader: storyblokLoader.getDatasourceLoader({
    datasource: "categories",
  }),
});

Since v0.0.4

This section documents all breaking changes introduced since version v0.0.4. If you're upgrading from v0.0.4 or earlier, please review these changes carefully.

1. Function Name Changes

StoryblokLoaderStoryblokLoaderStories
  • What changed: The main loader function has been renamed from StoryblokLoader to StoryblokLoaderStories
  • Reason: Better clarity and consistency as the package now supports multiple loader types (Stories and Datasources)
// ❌ Old (v0.0.4)
import { StoryblokLoader } from "astro-loader-storyblok";

const stories = defineCollection({
  loader: StoryblokLoader({ accessToken: "token" }),
});

// ✅ New (v0.1.0+)
import { StoryblokLoaderStories } from "astro-loader-storyblok";

const stories = defineCollection({
  loader: StoryblokLoaderStories({ accessToken: "token" }),
});

2. Enum Name Changes

SortBySortByEnum
  • What changed: The sorting enum has been renamed from SortBy to SortByEnum
  • Reason: Better naming convention and consistency
// ❌ Old (v0.0.4)
import { SortBy } from "astro-loader-storyblok";

const stories = defineCollection({
  loader: StoryblokLoader({
    accessToken: "token",
    sortBy: SortBy.CREATED_AT_DESC,
  }),
});

// ✅ New (v0.1.0+)
import { SortByEnum } from "astro-loader-storyblok";

const stories = defineCollection({
  loader: StoryblokLoaderStories(
    { accessToken: "token" },
    { sort_by: SortByEnum.CREATED_AT_DESC }
  ),
});

3. Configuration Structure Changes

Flattened configuration → Separated loader config and Storyblok API parameters
  • What changed: The configuration is now split into two parameters: loader-specific config and Storyblok API parameters
  • Reason: Better separation of concerns and more flexible API that directly maps to Storyblok's API parameters
// ❌ Old (v0.0.4)
const stories = defineCollection({
  loader: StoryblokLoader({
    accessToken: "token",
    version: "draft",
    contentTypes: ["article"],
    excludingSlugs: "home,about",
    sortBy: SortBy.CREATED_AT_DESC,
    useUuids: true,
    apiOptions: { region: "us" },
  }),
});

// ✅ New (v0.1.0+)
const stories = defineCollection({
  loader: StoryblokLoaderStories(
    {
      // Loader-specific configuration
      accessToken: "token",
      contentTypes: ["article"],
      useUuids: true,
      apiOptions: { region: "us" },
    },
    {
      // Standard Storyblok API parameters
      version: "draft",
      excluding_slugs: "home,about",
      sort_by: SortByEnum.CREATED_AT_DESC,
    }
  ),
});

4. Property Name Changes

Snake_case for Storyblok API parameters
  • What changed: Some properties now use snake_case to match Storyblok's API exactly
  • Reason: Direct mapping to Storyblok API parameters for consistency and better IntelliSense

| Old Property (v0.0.4) | New Property (v0.1.0+) | Parameter Location | |-----------------------|------------------------|-----------------------------------------------| | excludingSlugs | excluding_slugs | Storyblok API params (2nd parameter) | | sortBy | sort_by | Storyblok API params (2nd parameter) | | version | version | Moved to Storyblok API params (2nd parameter) |

5. Type Name Changes

Updated type definitions for better clarity
  • What changed: Several interface names have been updated to reflect the new structure
  • Reason: Better type organization and clarity
// ❌ Old (v0.0.4)
import type { StoryblokLoaderConfig } from "astro-loader-storyblok";

// ✅ New (v0.1.0+)
import type { 
  StoryblokLoaderStoriesConfig,
  StoryblokLoaderDatasourceConfig
} from "astro-loader-storyblok";

Migration Guide

⚠️ Deprecation Notice (v0.2.0)

The second parameter in StoryblokLoaderStories is deprecated. ~~While still functional with automatic backward compatibility~~, it will be removed in a future major version ✅.

Old (deprecated ~~but still works~~):

// ⚠️ This used to trigger a deprecation warning but will not work anymore
const stories = defineCollection({
  loader: StoryblokLoaderStories(config, { version: "draft" }),
});

New (recommended):

// ✅ Move storyblok parameters to config.storyblokParams
const stories = defineCollection({
  loader: StoryblokLoaderStories({
    ...config,
    storyblokParams: { version: "draft" },
  }),
});

Or use the helper function:

// ✅ Use the helper function for easier migration
import { createStoriesConfig } from "astro-loader-storyblok";

const stories = defineCollection({
  loader: StoryblokLoaderStories(
    createStoriesConfig(config, { version: "draft" }),
  )
});

The deprecation warning will guide you through the migration and provides automatic backward compatibility.


Migration from v0.0.4

To migrate from v0.0.4 to the latest version:

  1. Update import names:

    • StoryblokLoaderStoryblokLoaderStories
    • SortBySortByEnum
    • StoryblokLoaderConfigStoryblokLoaderStoriesConfig
  2. Restructure configuration:

    • Move version, excluding_slugs, sort_by to the second parameter
    • Keep accessToken, contentTypes, useUuids, apiOptions in the first parameter
  3. Update property names:

    • excludingSlugsexcluding_slugs
    • sortBysort_by
  4. Test your configuration: After making these changes, verify that your content loads correctly in both development and production environments.

Playground Example

Want to see it in action? Check out our minimal playground example in the playground/minimal-astro-project directory. This demonstrates a basic implementation that displays a table of stories from Storyblok. It can be used for testing and development.

To run the playground:

  1. Clone this repository and install dependencies:

    # Clone and setup
    git clone https://github.com/romainpi/astro-loader-storyblok.git
    cd astro-loader-storyblok
    pnpm install
  2. Set up your environment variables in the playground directory:

    cd playground/minimal-astro-project
    cp .env.example .env  # If available
    # Add your STORYBLOK_DELIVERY_PREVIEW_API_TOKEN to the .env file
  3. Start the development server: pnpm dev

  4. Open your browser to http://localhost:4321

The playground showcases:

  • Basic content collection configuration using the new StoryblokLoader class
  • Fetching and displaying stories in a simple table format
  • Environment variable setup for the Storyblok access token

TypeScript Support

This package is built with TypeScript and provides full type definitions, including the StorySortFunction type for
custom sorting.

For even better type safety, consider using storyblok-to-zod to generate Zod schemas for your Storyblok components.

import { z } from "astro:content";
import { StoryblokLoaderStories, type StorySortFunction } from "astro-loader-storyblok";
import { pageSchema } from "./types/storyblok.zod.ts";

// Example with Zod schema (when using storyblok-to-zod)
const stories = defineCollection({
  loader: StoryblokLoaderStories({
    accessToken: import.meta.env.STORYBLOK_TOKEN,
    storyblokParams: {
      version: "published",
    },
  }),
  schema: pageSchema,
});

Background

This Astro content loader is a community-driven successor to Storyblok’s archived Astro Content Layer integration. In September 2024, Storyblok had partnered with Astro for the launch of the Content Layer API and released an alpha version of a loader however, the implementation never made it to the mainline and was subsequently archived and remained in a premature state.

This package provides a complete, production-ready solution with full TypeScript support and works seamlessly with storyblok-to-zod for type-safe content schemas.

Verbose output / debugging

There are two ways of outputting debug messages from astro-loader-storyblok to console.

  1. Run astro with the --verbose flag in order to output all of Astro's and Vite's debug messages to console.

  2. Enable and filter only for messages from loader-storyblok with the DEBUG=astro:loader-storyblok* environment variable (more info). Example:

    DEBUG=astro:loader-storyblok* astro build

Feedback

Feedback and contributions are welcome! If you run into a problem, don't hesitate to open a GitHub issue.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT - see LICENSE.txt for details.