---
title: Integrate Eleventy with Storyblok
description: This guide walks you through integrating Storyblok as a headless CMS, fetching content, and building components to render it effectively in your Eleventy project.
url: https://www.storyblok.com/docs/guides/eleventy
---

# Integrate Eleventy with Storyblok

Use Storyblok to manage the content of your Eleventy website.

> [!NOTE]
> This guide has been tested with the following package versions:
> 
> -   `@11ty/eleventy@3.1.0`
> -   `@storyblok/js@4.0.0`
> -   `@storyblok/richtext@3.2.0`
> -   Node.js v22.13.0

## Setup

Create a new Eleventy project in a few simple steps by following the [Get Started](https://www.11ty.dev/docs/) page from its official documentation.

If you already have a Storyblok account, visit [app.storyblok.com](http://app.storyblok.com/#/signup) or [log in with GitHub](https://github.com/login?client_id=Iv23liC8pLXD6VcT2EbS&return_to=%2Flogin%2Foauth%2Fauthorize%3Fclient_id%3DIv23liC8pLXD6VcT2EbS%26redirect_uri%3Dhttps%253A%252F%252Fapp.storyblok.com%252F#/login) to continue.

Create a [new blank space](https://app.storyblok.com/#/me/spaces/new?tab=select-plan) to follow the tutorial from scratch, or start from the [core blueprint](https://app.storyblok.com/#/spaces/new/blueprint?blueprintReference=starter).

[Create one and start a free Storyblok space](https://app.storyblok.com/#/signup) No Storyblok account yet?

## Installation

In your terminal, `cd` into your Eleventy project and install the `@storyblok/js` and `dotenv` packages.

```bash
npm install @storyblok/js dotenv --save-dev
```

In the root of your project, create a `.env` file with the access token from your space.

```bash
STORYBLOK_DELIVERY_API_TOKEN="fqc3tdIuC8djNwEYl5cE5Att"
```

> [!TIP]
> Learn how to get an [access token](https://www.storyblok.com/docs/concepts/access-tokens#space-access-token) for your Storyblok project.

Every Eleventy project requires a config file at root. Create one with the following content.

.eleventy.config.js

```javascript
export default function eleventy() {

  return {
    dir: {
      input: 'src',
    },
  };
}
```

Create a `storyblok.js` file under the `src/_utils` directory and add this code:

src/\_utils/storyblok.js

```javascript
import { apiPlugin, storyblokInit } from '@storyblok/js';
import 'dotenv/config';

const { storyblokApi } = storyblokInit({
  accessToken: process.env.STORYBLOK_DELIVERY_API_TOKEN,
  apiOptions: {
    region: 'eu', // Choose the correct region from your Space.
  },
  use: [apiPlugin],
});

export default storyblokApi;
```

> [!WARNING]
> Ensure to set the correct `region` value depending on the server location of your Storyblok space. Learn more in the [@storyblok/js package reference](https://www.storyblok.com/docs/libraries/js/js-sdk).

The Storyblok module will make features like fetching, components registration and bridge available for your project.

## Fetch a single story

Create a `home.js` data file and use the client instance to fetch a story’s data.

src/\_data/home.js

```javascript
import storyblok from '../_utils/storyblok.js';

export default async function home() {
  const response = await storyblok.get("cdn/stories/home", {
    version: "draft", // or "published"
  });

  const { story } = response.data;
  const name = story.content.name;
  const body = story.content.body;

  return {
    name,
    body
  };
}
```

In a data file, we fetch a single story by its slug and return the content properties we want to use in our templates.

> [!NOTE]
> Learn more about how asynchronous data functions work in Eleventy on its [documentation page](https://www.11ty.dev/docs/data/).

Create a layout file as required by Eleventy to render a page.

src/\_includes/layouts/base.liquid

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{ title }}</title>
</head>
<body>
  {{ content }}
</body>
</html>
```

Use the name of the data file on a content file as a namespace to access properties in the content area, or in the front matter through `eleventyComputed` to pass them to the layout.

src/index.md

```html
---
layout: 'layouts/base.liquid'
eleventyComputed:
  title: '{{ home.name }}'
permalink: '/'
---

<main>
  {{ home.body }}
</main>
```

Eleventy can handle text based fields, and even Markdown with no configuration.

However, we need to deal with custom components within our space for a valid output.

## Create and register blocks

Stories might contain a `body` or similar field which consists of an array with several blocks of custom types (for example, Feature, Teaser, Grid) in it.

Create a function for each component that returns the resulting HTML for each.

src/\_components/feature.js

```javascript
function Feature(blok) {
  return (
    `<div class="feature">
       <span>${blok.name}</span>
     </div>`
  );
}

export default Feature;
```

src/\_components/teaser.js

```javascript
function Teaser(blok) {
  return (
    `<div class="teaser">
       <h2>${blok.headline}</h2>
     </div>`
  );
}

export default Teaser;
```

src/\_components/grid.js

```javascript
import Feature from './feature.js';
import Teaser from './teaser.js';

function Grid(blok) {
  return (
    `<div class="grid">
      ${blok.columns.map((nestedBlok) => {
        switch (nestedBlok.component) {
          case 'feature':
            return Feature(nestedBlok);
          case 'teaser':
            return Teaser(nestedBlok);
          default:
            throw new Error(`Grid could not resolve ${nestedBlok.component}.`);
        }
      }).join('')}
    </div>`
  );
}

export default Grid;
```

Iterate over the `body` array and resolve each component.

src/\_data/home.js

```javascript
import storyblok from '../_utils/storyblok.js';
import Feature from '../_components/feature.js'
import Teaser from '../_components/teaser.js'
import Grid from '../_components/grid.js'

export default async function home() {
  const response = await storyblok.get("cdn/stories/home", {
    version: "draft", // or "published"
  });

  const { story } = response.data;
  const name = story.content.name;
  const body = story.content.body
   .map(blok => {
     switch (blok.component) {
       case 'feature':
         return Feature(blok);
       case 'teaser':
         return Teaser(blok);
       case 'grid':
         return Grid(blok);
       default:
         throw new Error(`Could not resolve ${blok.component} block.`);
     }
   })
   .join('');

  return {
    name,
    body
  };
}
```

Similar to our `Grid` component, use the `body` to iterate over all blocks and render the corresponding one.

Run the server and visit the site in your browser.

```bash
npx @11ty/eleventy --serve
```

## Related resources

[Storyblok's Eleventy Blueprint Repository](https://github.com/storyblok/blueprint-core-eleventy)

[@storyblok/js Package Reference](/docs/libraries/js/js-sdk)

[Concept: Blocks](/docs/concepts/blocks)

[Content Delivery API: Retrieve a Single Story](/docs/api/content-delivery/v2/stories/retrieve-a-single-story)

[Eleventy Docs](https://www.11ty.dev/docs/)

  

## Pagination

-   [Next: Visual Preview in Eleventy](/docs/guides/eleventy/visual-preview)
