Storyblok Data Layer for Google Tag Manager | OpsBlu Docs

Storyblok Data Layer for Google Tag Manager

Configure GTM data layer with Storyblok story metadata, component data, and Visual Editor information for advanced tracking

A comprehensive data layer enables GTM to access Storyblok-specific information like story metadata, components, and Visual Editor state. This guide shows how to build a complete Storyblok data layer.

Storyblok Data Layer Overview

The data layer stores information about Storyblok stories, components, and user context. GTM tags access this data via variables.

Storyblok Data Categories

  • Story Data - Story ID, name, slug, publication dates
  • Component Data - Component types, UIDs, properties
  • Visual Editor Data - Editor mode status, preview state
  • Content Data - Titles, descriptions, metadata
  • Relationship Data - Linked stories and content relationships

Basic Data Layer Implementation

Nuxt 3

Create plugins/storyblok-data-layer.client.ts:

// plugins/storyblok-data-layer.client.ts
export default defineNuxtPlugin(() => {
  const route = useRoute();

  // Initialize dataLayer
  window.dataLayer = window.dataLayer || [];

  // Push comprehensive Storyblok data
  const pushStoryData = (story: any) => {
    window.dataLayer.push({
      event: 'storyblok_data_ready',

      // Page metadata
      page: {
        path: route.path,
        title: story.content.meta_title || story.name,
        description: story.content.meta_description,
      },

      // Storyblok story data
      storyblok: {
        // Core story info
        id: story.id,
        uuid: story.uuid,
        name: story.name,
        slug: story.slug,
        fullSlug: story.full_slug,
        component: story.content.component,

        // Publication metadata
        createdAt: story.created_at,
        publishedAt: story.published_at,
        firstPublishedAt: story.first_published_at,

        // Tags and categorization
        tagList: story.tag_list || [],

        // Language and localization
        lang: story.lang,
        alternates: story.alternates?.map((alt: any) => ({
          id: alt.id,
          name: alt.name,
          slug: alt.slug,
          lang: alt.lang,
        })) || [],

        // Visual Editor state
        isEditorMode: route.query._storyblok !== undefined,
      },
    });
  };

  return {
    provide: {
      pushStoryData,
    },
  };
});

Usage in page:

<!-- pages/[...slug].vue -->
<script setup>
const { slug } = useRoute().params;
const story = await useAsyncStoryblok(slug.join('/'), { version: 'draft' });
const { $pushStoryData } = useNuxtApp();

onMounted(() => {
  if (story.value) {
    $pushStoryData(story.value);
  }
});
</script>

Advanced Data Layer Patterns

Component-Level Data Layer

Track individual component data:

<!-- components/HeroSection.vue -->
<script setup>
const { blok } = defineProps(['blok']);

onMounted(() => {
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({
    event: 'component_loaded',
    component: {
      type: blok.component,
      uid: blok._uid,
      // Extract simple field values
      hasImage: !!blok.image,
      hasVideo: !!blok.video,
      ctaCount: blok.cta_buttons?.length || 0,
    },
  });
});
</script>

Visual Editor Detection

// composables/useEditorTracking.ts
export const useEditorTracking = () => {
  const route = useRoute();
  const isEditor = computed(() => route.query._storyblok !== undefined);

  watch(isEditor, (inEditor) => {
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({
      event: 'editor_mode_status',
      editor: {
        active: inEditor,
        timestamp: new Date().toISOString(),
      },
    });
  }, { immediate: true });

  return { isEditor };
};

Story Relationships Data Layer

const pushRelationships = (story: any) => {
  const relationships = {};

  // Extract relationship fields
  Object.keys(story.content).forEach(key => {
    const field = story.content[key];

    if (field && typeof field === 'object' && field.id && field.component) {
      relationships[key] = {
        id: field.id,
        name: field.name,
        component: field.component,
        slug: field.slug,
      };
    }
  });

  if (Object.keys(relationships).length > 0) {
    window.dataLayer.push({
      event: 'storyblok_relationships',
      relationships,
    });
  }
};

GTM Variable Configuration

Create Data Layer Variables in GTM

Navigate to Variables → New → User-Defined Variables:

Story Variables

Variable Name Type Data Layer Variable Name
Storyblok Story ID Data Layer Variable storyblok.id
Storyblok Story Name Data Layer Variable storyblok.name
Storyblok Component Data Layer Variable storyblok.component
Storyblok Language Data Layer Variable storyblok.lang
Storyblok Tags Data Layer Variable storyblok.tagList

Editor Variables

Variable Name Type Data Layer Variable Name
Editor Mode Active Data Layer Variable storyblok.isEditorMode
Editor Timestamp Data Layer Variable editor.timestamp

GTM Trigger Configuration

Story Type Triggers

Trigger: Blog Posts

  • Type: Custom Event
  • Event name: storyblok_data_ready
  • Condition: storyblok.component equals blog_post

Trigger: Landing Pages

  • Type: Custom Event
  • Event name: storyblok_data_ready
  • Condition: storyblok.component equals landing_page

Editor Mode Trigger

Trigger: Editor Active

  • Type: Custom Event
  • Event name: editor_mode_status
  • Condition: storyblok.isEditorMode equals true

Using Data Layer in GTM Tags

GA4 Configuration with Storyblok Data

Tag: GA4 Configuration

  • Tag Type: Google Analytics: GA4 Configuration
  • Measurement ID: G-XXXXXXXXXX
  • Configuration Parameters:
    • story_component: \{\{Storyblok Component\}\}
    • story_name: \{\{Storyblok Story Name\}\}
    • content_language: \{\{Storyblok Language\}\}
    • editor_mode: \{\{Editor Mode Active\}\}

Testing and Debugging

Preview Data Layer in Console

// In browser console
console.log(window.dataLayer);

// Find Storyblok data
console.log(window.dataLayer.find(item => item.event === 'storyblok_data_ready'));

GTM Preview Mode

  1. In GTM, click Preview
  2. Enter your Storyblok site URL
  3. In Tag Assistant:
    • Verify storyblok_data_ready event fires
    • Check Data Layer tab
    • Verify all Storyblok variables populate

Performance Considerations

Minimize Data Layer Size

// Good: Only metadata
window.dataLayer.push({
  storyblok: {
    id: story.id,
    component: story.content.component,
    name: story.name,
  },
});

// Bad: Too much data
window.dataLayer.push({
  storyblok: {
    fullContent: story.content, // Don't include full content
  },
});

Next Steps