Features

Live Preview

Enable real-time content editing with Contentstack's Live Preview and Visual Builder integration for seamless content management workflows.

Live Preview is one of the most powerful features of Nuxt Contentstack, enabling real-time content editing and visual building capabilities. Content creators can see their changes instantly without page refreshes, creating a seamless editing experience.

Learn Live Preview: Master Contentstack's Live Preview feature with the Implementing Live Preview Academy course. Learn setup and configuration for both Client-Side Rendering (CSR) and Server-Side Rendering (SSR) environments.

Overview

Live Preview consists of several integrated features:

  • Real-time Updates: See content changes instantly without page refreshes
  • Visual Builder: Edit content directly on the page with visual editing tools
  • Edit Buttons: Quick access to editing interfaces
  • Editable Tags: Visual indicators for editable content areas
  • Preview Modes: Switch between builder and preview modes

Quick Setup

1. Enable Live Preview

Configure live preview in your nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-contentstack'],
  
  'nuxt-contentstack': {
    // Core configuration
    apiKey: process.env.NUXT_CONTENTSTACK_API_KEY,
    deliveryToken: process.env.NUXT_CONTENTSTACK_DELIVERY_TOKEN,
    environment: 'preview', // Use preview environment
    
    // Live Preview configuration
    livePreview: {
      enable: true,
      previewToken: process.env.NUXT_CONTENTSTACK_PREVIEW_TOKEN,
      editableTags: true,
      editButton: true,
      mode: 'builder'
    }
  }
})

2. Environment Variables

Add your preview token to environment variables:

.env
NUXT_CONTENTSTACK_API_KEY=your_api_key
NUXT_CONTENTSTACK_DELIVERY_TOKEN=your_delivery_token
NUXT_CONTENTSTACK_PREVIEW_TOKEN=your_preview_token
NUXT_CONTENTSTACK_ENVIRONMENT=preview
Preview Token: Find your preview token in Contentstack under Settings → Tokens → Preview Tokens.

3. Update Your Components

Add CSLP (Contentstack Live Preview) attributes to make content editable:

pages/[...slug].vue
<script setup>
import type { Page } from "../../types";

type PageProps = Omit<Page, "$"> & {
  cslp?: Page["$"];
};

const route = useRoute()
const path = `/${route.params.slug?.join('/') || ''}`

// Automatically supports live preview
const { data: page, refresh } = await useGetEntryByUrl<PageProps>({
  contentTypeUid: 'page',
  url: path,
  referenceFieldPath: ['components', 'seo'],
  jsonRtePath: ['content']
})

if (!page.value) {
  throw createError({ statusCode: 404, statusMessage: 'Page Not Found' })
}
</script>

<template>
  <main v-if="page">
    <h1 v-bind="page.cslp && page.cslp.title">{{ page.title }}</h1>
    <div v-bind="page.cslp && page.cslp.content" v-html="page.content"></div>
    
    <!-- Components with live preview support -->
    <component 
      v-for="comp in page.components" 
      :key="comp.uid"
      :is="comp._content_type_uid"
      v-bind="comp"
    />
  </main>
</template>
CSLP Attributes: The v-bind="page.cslp && page.cslp.fieldName" pattern adds data-cslp attributes that enable in-place editing in Contentstack's Visual Builder.

Configuration Options

Basic Configuration

livePreview: {
  enable: true,                    // Enable/disable live preview
  previewToken: 'your_token',      // Required preview token
  editableTags: true,              // Show editable content indicators
  editButton: true,                // Show edit button
  mode: 'builder',                 // 'builder' or 'preview'
  ssr: false                       // Server-side rendering mode
}

Advanced Edit Button Configuration

Customize the edit button appearance and behavior:

livePreview: {
  enable: true,
  previewToken: 'your_token',
  editButton: {
    enable: true,
    position: 'top-right',               // Button position
    exclude: [                           // Where to hide the button
      'insideLivePreviewPortal',
      'outsideLivePreviewPortal'
    ],
    includeByQueryParameter: false       // Show only with ?edit=true
  }
}

Edit Button Positions

PositionDescription
topTop center
bottomBottom center
leftLeft center
rightRight center
top-leftTop left corner
top-rightTop right corner
top-centerTop center
bottom-leftBottom left corner
bottom-rightBottom right corner
bottom-centerBottom center

Live Preview Modes

Builder Mode

Builder mode provides the full visual editing experience:

livePreview: {
  mode: 'builder'  // Full editing capabilities
}

Features:

  • Visual content editing
  • Drag-and-drop components
  • Real-time preview
  • Component property editing

Preview Mode

Preview mode shows content updates without editing interface:

livePreview: {
  mode: 'preview'  // Preview only, no editing UI
}

Features:

  • Real-time content updates
  • No editing interface
  • Clean preview experience
  • Content validation

Working with Components

Basic Component Setup

Create components that work seamlessly with Live Preview by adding CSLP attributes:

components/content/Hero.vue
<script setup>
// Define props with CSLP support
interface Props {
  title: string
  subtitle?: string
  background_image?: {
    url: string
    title: string
    cslp?: {
      url?: any
      title?: any
    }
  }
  cta_button?: {
    text: string
    url: string
    cslp?: {
      text?: any
      url?: any
    }
  }
  cslp?: {
    title?: any
    subtitle?: any
  }
}

const props = defineProps<Props>()
</script>

<template>
  <section class="hero">
    <div 
      v-if="background_image" 
      class="hero-background"
      :style="{ backgroundImage: `url(${background_image.url})` }"
      v-bind="background_image.cslp && background_image.cslp.url"
    >
      <div class="hero-content">
        <h1 v-bind="props.cslp && props.cslp.title">{{ title }}</h1>
        <p v-if="subtitle" v-bind="props.cslp && props.cslp.subtitle">{{ subtitle }}</p>
        
        <NuxtLink 
          v-if="cta_button" 
          :to="cta_button.url"
          class="cta-button"
          v-bind="cta_button.cslp && (cta_button.cslp.text || cta_button.cslp.url)"
        >
          {{ cta_button.text }}
        </NuxtLink>
      </div>
    </div>
  </section>
</template>

Modular Block Components

For modular content blocks, create a component mapper:

components/ContentBlocks.vue
<script setup>
defineProps<{
  blocks: Array<{
    _content_type_uid: string
    uid: string
    [key: string]: any
  }>
}>()

// Map content type UIDs to component names
const componentMap = {
  'hero_block': 'Hero',
  'text_block': 'TextBlock',
  'image_gallery': 'ImageGallery',
  'cta_block': 'CtaBlock'
}
</script>

<template>
  <div class="content-blocks">
    <component
      v-for="block in blocks"
      :key="block.uid"
      :is="componentMap[block._content_type_uid] || 'UnknownBlock'"
      v-bind="block"
    />
  </div>
</template>

Rich Text with Live Preview

Handle rich text content with live preview support. CSLP attributes are automatically added to HTML content:

components/RichTextContent.vue
<script setup>
interface Props {
  content: string
  replaceHtmlCslp?: boolean
  cslp?: {
    content?: any
  }
}

const props = defineProps<Props>()
</script>

<template>
  <div 
    class="rich-text-content"
    v-bind="props.cslp && props.cslp.content"
    v-html="content"
  />
</template>

<style>
/* Ensure live preview styles don't interfere */
.rich-text-content {
  /* Your content styles */
}

/* CSLP attributes are automatically added to HTML elements inside rich text */
.rich-text-content [data-cslp] {
  /* Live preview will handle these for in-place editing */
}
</style>

Environment-Specific Configuration

Development Setup

Enable live preview only in development:

nuxt.config.ts
const isDev = process.env.NODE_ENV === 'development'

export default defineNuxtConfig({
  'nuxt-contentstack': {
    // ... core config
    livePreview: {
      enable: isDev,
      previewToken: process.env.NUXT_CONTENTSTACK_PREVIEW_TOKEN,
      editableTags: isDev,
      editButton: isDev
    }
  }
})

Staging Environment

Configure live preview for staging environments:

nuxt.config.ts
const isStaging = process.env.NODE_ENV === 'staging'
const enableLivePreview = process.env.ENABLE_LIVE_PREVIEW === 'true'

export default defineNuxtConfig({
  'nuxt-contentstack': {
    environment: isStaging ? 'preview' : 'production',
    livePreview: {
      enable: enableLivePreview,
      previewToken: process.env.NUXT_CONTENTSTACK_PREVIEW_TOKEN,
      editButton: {
        enable: enableLivePreview,
        includeByQueryParameter: true  // Show only with ?edit=true
      }
    }
  }
})

Advanced Usage

Manual Live Preview Integration

For advanced use cases, access the Live Preview SDK directly:

composables/useLivePreview.ts
export const useLivePreview = () => {
  const { livePreviewEnabled, ContentstackLivePreview } = useContentstack()

  const initLivePreview = () => {
    if (import.meta.client && livePreviewEnabled) {
      // Custom live preview logic
      ContentstackLivePreview.onEntryChange((data) => {
        console.log('Entry changed:', data)
        // Custom handling
      })
    }
  }

  return {
    initLivePreview,
    livePreviewEnabled
  }
}

Custom Edit Buttons

Create custom edit buttons for specific content areas:

components/EditableSection.vue
<script setup>
const props = defineProps<{
  entryUid: string
  contentTypeUid: string
  title: string
}>()

const { livePreviewEnabled } = useContentstack()

const editUrl = computed(() => {
  if (!livePreviewEnabled) return null
  
  return `https://app.contentstack.com/#!/stack/${stackApiKey}/content-type/${props.contentTypeUid}/en-us/entry/${props.entryUid}/edit`
})
</script>

<template>
  <div class="editable-section">
    <div class="content-header">
      <h2>{{ title }}</h2>
      <a 
        v-if="editUrl" 
        :href="editUrl" 
        target="_blank"
        class="edit-button"
      >
        ✏️ Edit
      </a>
    </div>
    
    <slot />
  </div>
</template>

Best Practices

Content Structure

Modular Design:

  • Create reusable component blocks
  • Use consistent naming conventions
  • Structure content hierarchically

Field Naming:

  • Use descriptive field names
  • Follow camelCase or snake_case consistently
  • Avoid special characters in field names

Performance

Efficient Updates:

  • Use specific reference paths
  • Avoid deep nesting where possible
  • Implement proper error boundaries

Caching Strategy:

  • Cache static content appropriately
  • Use cache invalidation for live preview
  • Consider preview vs. production caching

User Experience

Visual Feedback:

  • Show loading states during updates
  • Provide clear editing indicators
  • Handle error states gracefully

Editor Experience:

  • Provide meaningful component names
  • Use descriptive help text
  • Implement validation feedback

Troubleshooting

Common Issues

Live Preview Not Working:

// Check configuration
const { livePreviewEnabled } = useContentstack()
console.log(livePreviewEnabled)

// Verify preview token
// Ensure environment is set to 'preview'
// Check browser console for errors

Edit Button Not Appearing:

// Verify edit button configuration
livePreview: {
  editButton: {
    enable: true,
    position: 'top-right'
  }
}

Content Not Updating:

// Ensure composables support refresh
const { data, refresh } = await useGetEntry(...)

// Check live preview event listeners
ContentstackLivePreview.onEntryChange(refresh)

Debug Mode

Enable debug mode for detailed live preview logging:

nuxt.config.ts
'nuxt-contentstack': {
  debug: true,
  livePreview: {
    enable: true,
    // ... other config
  }
}

Next Steps

Personalization Setup

Configure personalization for targeted content delivery.