Was this page helpful?

Live preview - Inspector mode

Table of contents

Overview

Inspector mode is an advanced live preview feature that allows you to access an entry that contains a specific piece of website content with one click. When you click "Edit" button beside a content block in the preview iframe, you are redirected to the corresponding field within the entry. This is particularly helpful when authors share the preview URL with colleagues, as it enables them to access the correct Contentful entry with a single click.

If the field contains reference to another entry, you are redirected to the referenced entry editor.

Live preview inspector mode

Inspector mode setup options

Inspector mode is set up by tagging fields. This tagging can be done according to one of the following options:

  • Recommended: Automatically with Content Source Maps - Tagging is done automatically, hidden strings are included as tagging information. Content Source Maps tag text fields, Rich Text fields, and assets.
Content Source Maps are only available on our Premium plan.
  • Manually - corresponding fields are tagged manually by adding data attributes.
We strongly recommend Content Source Maps option since tagging is automated, and the setup is quick - 30 minutes to a day for the most common use cases and content setups. Content Source Maps provide scalability - they also work when new content types are created in the future.

To better understand the difference between the two approaches, view their main characteristics in the table below:

Characteristic Content Source Maps (recommended) Manual tagging
Pricing plan Premium All users
Supported fields Text, Rich text, list of Short text, assets All field types
Setup time 30 minutes to a day Significantly more time
Maintenance Scales to new content types Needs to be adapted for new content types

Set up inspector mode

To enable inspector mode, it is required to set up live preview first.

Content Source Maps

How it works

When enabled, our APIs return source maps for certain visual fields that have links to the correct field in Contentful. The Live Preview SDK then transforms these Content Source Maps to hidden metadata into the results of your queries.

This integration employs a method known as steganography, which conceals hidden information within other data. Specifically, this approach embeds the metadata into invisible Unicode characters incorporated into the existing text strings.

The use of these "enhanced strings" on your web pages allows live preview to detect and decipher the concealed metadata in the final HTML. This enables the display of interactive links that guide users directly to the relevant field in Contentful.

Supported field types

Field Type Status Notes
Short text (Symbol) Supported Only "Single line" Appearance setting *
Long Text Supported Only "Single line" and "Multiple line" Appearance setting *
Rich Text Supported Includes embedded references and assets
Lists of Short text (Symbol) Supported Supports all Appearance settings
Title and Description fields in an asset Supported Must be used as alt attribute for images
Custom widgets of supported field types Supported Can be tagged with Content Source Maps
URL values Not supported Adding hidden metadata would cause issues
ISO Date values Not supported Adding hidden metadata would cause issues
Cross-space content Not currently supported
*Unsupported widget appearance settings:
  • Dropdown and Radio: Not supported due to their primary use for non-visual text purposes (e.g., CSS styling properties).
  • URL and Slug: Not supported as hidden metadata would cause issues.
  • Markdown is not supported.

Implementation

Content Source Maps can be set up according to one of the following options:

GraphQL
  1. Install the latest version of the Live Preview SDK.
npm install @contentful/live-preview
  1. Initialize the SDK.
   import { ContentfulLivePreviewProvider } from
   '@contentful/live-preview/react';
   const CustomApp = ({ Component, pageProps }) => (
     <ContentfulLivePreviewProvider locale="en-US">
       <Component {...pageProps}>
     </ContentfulLivePreviewProvider>
)
  1. Use the query level directive in your GraphQL queries.
Content Source Maps will only be generated for preview content.

Content Source Maps return from our GraphQL API under the “extensions”:

query @contentSourceMaps { //Add the query level directive
      postCollection(preview: true) { //And preview: true
         items { 
            title
         } 
    }
}
   // This will return:
   {
     "data": ...
     "extensions": ... //Content Source Maps will be here
}
  1. Use the encodeGraphQLResponse function from the Live Preview SDK by passing it the GraphQL Response with Content Source Maps. It will return with your content that includes the hidden metadata to enable inspector mode.
import { encodeGraphQLResponse } from "@contentful/live-preview";
const dataWithAutoTagging = encodeGraphQLResponse(data);
Content Preview API (CPA)
  • With Contentful.js SDK (recommended):
  1. Initialize the Live Preview SDK
   import { ContentfulLivePreviewProvider } from
   '@contentful/live-preview/react';
   const CustomApp = ({ Component, pageProps }) => (
     <ContentfulLivePreviewProvider locale="en-US">
       <Component {...pageProps}>
     </ContentfulLivePreviewProvider>
   )
  1. Enable Content Source Maps using the Contentful Client SDK, by setting includeContentSourceMaps in the client to true:
export const clientPreview = createClient({
space: process.env.CONTENTFUL_SPACE_ID!,
          accessToken: process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN!,
     host: "preview.contentful.com",
       includeContentSourceMaps: true
});
  • Without Contentful.js SDK:
  1. Initialize the Live Preview SDK
   import { ContentfulLivePreviewProvider } from
   '@contentful/live-preview/react';
   const CustomApp = ({ Component, pageProps }) => (
     <ContentfulLivePreviewProvider locale="en-US">
       <Component {...pageProps}>
     </ContentfulLivePreviewProvider>
   )
  1. Directly access the Contentful CPA without using the Contentful.js SDK.

Please be aware that without the Contentful Client SDK, certain protections, such as automatically requesting the sys.id, are not enforced. To ensure Content Source Maps function properly, the complete sys object needs to be retrieved. Therefore, using a select operator to exclude this from the response would cause errors.

  1. Add &includeContentSourceMaps=true to the URL:
fetch("https://preview.contentful.com/spaces/:spaceId/environments/:e nvId/entries&includeContentSourceMaps=true",
    {
      method: "GET",
      headers: {
        Authorization: "Bearer YOUR_ACCESS_TOKEN",
        Content-Type: "application/json",
      },
} )
  1. Use the encodeCPAResponse function from the Live Preview SDK by passing it the CPA Response with Content Source Maps. It will return with your content that includes the hidden metadata to enable inspector mode.
   import { encodeCPAResponse } from "@contentful/live-preview";
   const dataWithAutoTagging = encodeCPAResponse(data);

Verifying Content Source Maps in API Responses

To verify that Content Source Maps are being returned correctly by our APIs, look for specific fields in the API response. The APIs provide raw Content Source Maps, which the Contentful.js SDK or Live Preview SDK transforms into hidden characters embedded within your content. These hidden characters enable inspector mode functionality.

GraphQL

When using the GraphQL API, ensure that you add the query-level directive @contentSourceMaps to the query. Additionally, preview content should be requested by setting the preview: true argument. Content Source Maps will appear under the extensions object of the response.

Example of GraphQL Response:

{
  "data": {
    "post": {
      "title": "Title of the post",
      "subtitle": "Subtitle of the post"
    }
  },
  "extensions": {
    "contentSourceMaps": {
      "version": 1.0,
      "spaces": ["foo"],
      "environments": ["master"],
      "fields": ["title", "subtitle"],
      "locales": ["en-US"],
      "entries": [{ "space": 0, "environment": 0, "id": "a1b2c3" }],
      "assets": [],
      "mappings": {
        "/data/post/title": {
          "source": {
            "entry": 0,
            "field": 0,
            "locale": 0
          }
        },
        "/data/post/subtitle": {
          "source": {
            "entry": 0,
            "field": 1,
            "locale": 0
          }
        }
      }
    }
  }
}
Content Preview API (CPA)

When querying the Content Preview API (CPA), verify the presence of contentSourceMapsLookup and contentSourceMaps fields within the sys object in the response.

Example of CPA Response:

{
  "sys": {
    "id": "3cmDYq19b7cY5FBsAETyTu",
    "type": "Entry",
    "contentSourceMapsLookup": {
      "sys": {
        "type": "ContentSourceMapsLookup"
      },
      "fieldTypes": ["symbol"],
      "editorInterfaces": [
        {
          "widgetId": "singleLine",
          "widgetNamespace": "builtin"
        }
      ]
    },
    "contentSourceMaps": {
      "sys": {
        "type": "ContentSourceMaps"
      },
      "mappings": {
        "/fields/title": {
          "source": {
            "fieldType": 0,
            "editorInterface": 0
          }
        }
      }
    }
  },
  "fields": {
    "title": {
      "en-US": "hello en-US",
      "de": "hallo de"
    }
  }
}

Troubleshooting and tips

We've taken a cautious approach in determining which fields to generate Content Source Maps for, ensuring that it won't disrupt your website. The remaining potential issues might include the following:

  • Under certain circumstances, such as when applying letter-spacing in CSS, fields may display styles that weren't intended. Additionally, strict string comparisons can fail now that hidden metadata is appended to the values. In these cases, you can utilize the functions provided by the Live Preview SDK to retrieve the content and remove any hidden metadata.
import { splitEncoding } from "@contentful/live-preview";
const { cleaned, encoded } = splitEncoding(text);

Known issues and limitations

  • Adding hidden metadata to content can result in problems, e.g. when being used for CSS values, for dates or URL content. You can remove the hidden strings using the splitEncoding function from the Live Preview SDK.
  • Encoding is skipped on the following formats:
    • Any date format that does not use English letters (e.g. 4/30/24);
    • ISO dates (e.g. 2024-04-30T12:34:59Z);
    • URLs.
  • We intentionally avoid tagging certain widgets, such as dropdowns, because they are primarily used for non-visual text purposes, such as CSS styling properties (e.g. #f4f4f4 color code). Adding hidden strings to these fields could potentially cause issues.
  • We do not support Gatsby with the gatsby-source-contentful plugin, since it uses the Sync API under the hood. Content Source Maps currently only get generated for the CPA and GraphQL API.

Manual tagging

Implementation with Vanilla JS and React

  1. Initialize the live preview SDK with the live preview provider.
import { ContentfulLivePreview } from '@contentful/live-preview';

ContentfulLivePreview.init({
  locale: 'en-US', // This is required and allows you to set the locale once and have it reused throughout the preview
  enableInspectorMode: false, // This allows you to toggle the inspector mode which is on by default
  enableLiveUpdates: false, // This allows you to toggle the live updates which is on by default
  debugMode: false, // This allows you to toggle the debug mode which is off by default
  targetOrigin: 'https://app.contentful.com', // This allows you to configure the allowed host of the live preview (default: ['https://app.contentful.com', 'https://app.eu.contentful.com'])
});
import { ContentfulLivePreviewProvider } from "@contentful/live-preview/react";

const App = ({ Component, pageProps }) => (
  <ContentfulLivePreviewProvider
    locale="en-US" // This is required and allows you to set the locale once and have it reused throughout the preview
    enableInspectorMode // This allows you to toggle the inspector mode which is on by default
    enableLiveUpdates // This allows you to toggle the live updates which is on by default
    debugMode // This allows you to toggle the debug mode which is off by default
    targetOrigin="https://app.contentful.com" // This allows you to configure the allowed host of the live preview (default: ['https://app.contentful.com', 'https://app.eu.contentful.com'])
  >
    <Component {...pageProps} />
  </ContentfulLivePreviewProvider>
);
  1. Tag the corresponding field(s). To tag a field, specify its location within the entry by writing data attributes. The live preview SDK scans for these elements containing the correct field tags.
The manual tagging only works with fields that have the correct data attributes. Fields without these data attributes aren't seen as tagged and don't work with this feature.

The required data attributes can be added according to one of the following options:

  • HTML: tag fields by adding certain data attributes to HTML elements:
<h1
data-contentful-field-id="field-id"
data-contentful-entry-id="entry-id" // or data-contentful-asset-id
data-contentful-locale="fr-FR" // Optional: override the locale you have defined in the provider
data-contentful-space="cross-space" // Optional: only required for tagging content from a different space
data-contentful-environment="cross-env" // Optional: only required for tagging content from a different space
></h1>
  • Vanilla JS/React We have some helper utilities for different frameworks to make this process easier. These helper utilities will help the user add the data attributes to the HTML elements:
import { ContentfulLivePreview } from '@contentful/live-preview';

<h1
  {...ContentfulLivePreview.getProps({
    entryId: id,
    fieldId: 'title',
    space: 'otherSpaceId', // Optional: only required for tagging content from a different space
    environment: 'otherEnvironmentId' // Optional: only required for tagging content from a different space
  })}
>
  {title}
</h1>;
import { useContentfulInspectorMode } from '@contentful/live-preview/react';

export default function BlogPost ({ post }) {
  const inspectorProps = useContentfulInspectorMode({ entryId: post.sys.id })
  return (
    <section>
      <h1 {...inspectorProps({ fieldId: 'title' })}>{post.fields.title}</h1>
    </section>
  )
}

Implementation with Next.js app router

  1. Create a Next.js client component where you initialize the ContentfulLivePreviewProvider from the SDK.
"use client";

import { ContentfulLivePreviewInitConfig } from "@contentful/live-preview";
import { ContentfulLivePreviewProvider } from "@contentful/live-preview/react";
import { PropsWithChildren } from "react";

export function ContentfulPreviewProvider({
  children,
  ...props
}: PropsWithChildren<ContentfulLivePreviewInitConfig>) {
  return (
    <ContentfulLivePreviewProvider {...props}>
      {children}
    </ContentfulLivePreviewProvider>
  );
}

Once the provider is set up in the client component, you can wrap your server component app with it:

import { ContentfulPreviewProvider } from "../components/contentful-preview-provider";

export default function Home() {
  const { isEnabled } = draftMode();
  return (
    <ContentfulLivePreviewProvider
      locale="en-US" // This is required and allows you to set the locale once and have it reused throughout the preview
      enableInspectorMode // This allows you to toggle the inspector mode which is on by default
      enableLiveUpdates // This allows you to toggle the live updates which is on by default
      debugMode // This allows you to toggle the debug mode which is off by default
      targetOrigin="https://app.contentful.com" // This allows you to configure the allowed host of the live preview (default: ['https://app.contentful.com', 'https://app.eu.contentful.com'])
    >
      <main>...</main>
    </ContentfulLivePreviewProvider>
  );
}

For a full example visit the examples directory on Github.

  1. We have a helper utility to make the process of manually tagging fields easier. This helper utility will help you to add the data attributes to the HTML elements:
import { useContentfulInspectorMode } from '@contentful/live-preview/react';

export default function BlogPost ({ post }) {
  const inspectorProps = useContentfulInspectorMode({ entryId: post.sys.id })
  return (
    <section>
      <h1 {...inspectorProps({ fieldId: 'title' })}>{post.fields.title}</h1>
    </section>
  )
}

Tag cross-space content

Live preview inspector mode

To tag cross-space content, add data-contentful-space and data-contentful-environment as additional data attributes to the fields you want to tag. Cross-space content will be shown as gray boxes in inspector mode instead of blue. The entry opens in a new tab where you can make changes to it.

import { ContentfulLivePreview } from '@contentful/live-preview';

<h1
  {...ContentfulLivePreview.getProps({
    entryId: id,
    fieldId: 'title',
    space: 'otherSpaceId', // Optional: only required for tagging content from a different space
    environment: 'otherEnvironmentId' // Optional: only required for tagging content from a different space
  })}
>
  {title}
</h1>;
import { useContentfulInspectorMode } from '@contentful/live-preview/react';

export default function BlogPost: ({ blogPost }) {
  const inspectorProps = useContentfulInspectorMode({ entryId: blogPost.sys.id })

  return (
    <Section>
      <Heading as="h1" {...inspectorProps({ fieldId: 'heading', space: "otherSpaceId", environment: "otherEnvironmentId" })}>{blogPost.fields.heading}</Heading>
    </Section>
  )
}

Provider configuration

The ContentfulLivePreviewProvider accepts parameters that allow you to customize your live preview SDK experience. The following options are available:

import { ContentfulLivePreviewProvider } from '@contentful/live-preview/react';

<ContentfulLivePreviewProvider
  locale="set-your-locale-here" // Required: allows you to set the locale once and have it reused throughout the preview.
  enableInspectorMode={false} // Optional: allows you to toggle inspector mode which is on by default.
  enableLiveUpdates={false} // Optional: allows you to toggle live updates which are on by default.
  targetOrigin="https://app.contentful.com" // Optional: allows you to configure the allowed host(s) of live preview (default: ['https://app.contentful.com', 'https://app.eu.contentful.com'])
  debugMode={false} // Optional: allows you to toggle debug mode which is off by default.
>

To optimize non-preview websites, it is advisable to disable live preview functionality by setting both enableInspectorMode and enableLiveUpdates to "false". By doing so, any specific data related to live preview, such as data-attributes, is removed.

Best practices

We recommend to tag fields as follows:

  • Tag larger elements — Only tag elements of the page that are significantly larger than the mouse cursor. Tagging smaller elements such as icons inside buttons can result in a very fragmented tagging surface and we recommend to rather only tag the button itself then.

  • Nested entries — Don't tag pages further than 3 nesting levels. We believe that there is a tipping point where additional tagging of nested entries is not beneficial for the user experience since moving the mouse slightly will potentially show you various "Edit" buttons of different content pieces.

  • References — Tag the whole referenced entry and the main content inside for faster editing. In case where the text inside is short - for example, the reference is a circle with a one-word text inside — then the recommendation is to refrain from tagging this text.

  • Website sections — We definitely recommend tagging the sections of a page.

  • Styling — Many customers manage styling parameters with Contentful, e.g. the background color for a section or the margins around texts. We generally don’t recommend tagging styling as it might result in a confusing user experience, as hover areas intersect to a large extent or fully with the element the styling applies to.