Preview Plugin

Previewing Experiences before they are published.

The Preview Plugin allows you to assign your profile to specific audiences, experiences, and variants within preview and development contexts.

Add plugin to the plugins prop of the NinetailedProvider and provide an array of all Ninetailed Experience and Ninetailed Audience entries to the plugin. Ensure that you are supplying both all published and all draft entries of each to the plugin. Additionally, you should ensure that you are not instantiating the plugin in production contexts.

Installation

Add the dependency:

npm install @ninetailed/experience.js-plugin-preview

Then, add the plugin to the Ninetailed instance:

import { NinetailedPreviewPlugin } from '@ninetailed/experience.js-plugin-preview'
<NinetailedProvider
  //...
  plugins={[
    new NinetailedPreviewPlugin({
      // Required: Experiences from your CMS
      experiences: yourExperiences || [],
      // Required: Audiences from your CMS
      audiences: yourAudiences || [],
      // Optional: Callback to handle user forwarding to the experience entry in your CMS
      onOpenExperienceEditor: (experience) => {},
      // Optional: Callback to handle user forwarding to the audience entry in your CMS
      onOpenAudienceEditor: (audience) => {},
      // Optional: Determine the visibility of the Preview Plugin
      ui: { opener: { hide: false } },
    })
  ]}
>
  // ...
</NinetailedProvider>

Conditional Instantiation

The Preview Plugin is designed to be used within preview and development contexts. This plugin does not automatically turn off in production contexts; you are responsible for instantiating the plugin only when it is required.

One way of doing this is through a simple instantiation toggle based on some runtime argument. The code sample below shows conditionally including the plugin only when process.env.NODE_ENV !== 'production', but you are free to use whatever condition best identifies your non-production environments. For example, you might choose to instantiate the plugin based on whether the current visitor has turned on preview or draft mode and use this condition instead or in conjunction.

const preview = process.env.NODE_ENV !== 'production'
<NinetailedProvider
  ... // Other provider props
  plugins={[
    ...(preview
      ? [
          new NinetailedPreviewPlugin({
            experiences:
            audiences: pageProps.ninetailed?.preview.allAudiences || [],
          }),
        ]
      : []),
    ... // Other plugins
  ]}
>
  ... // Children of provider
</NinetailedProvider>

However, this simple toggle will bundle the preview plugin in production settings. A more robust way would be to dynamically import the preview module only when it is required. The below Next.js code sample shows returning a <NinetailedProvider> component with the Preview Plugin only when preview data is supplied, leveraging next/dynamic to exclude the depedency from the bundle.

lib/getNtProvider.tsx
import type { NinetailedProviderProps } from '@ninetailed/experience.js-next';
import type { PropsWithChildren } from 'react';

import dynamic from 'next/dynamic';
import { NinetailedProvider } from '@ninetailed/experience.js-next';

/**
 * Dynamic import of the preview plugin ensures it it not part of a production bundle
 */
export function getNinetailedProvider(enablePreview: boolean) {
  const providerProps: NinetailedProviderProps = {
    clientId: process.env.NEXT_PUBLIC_NINETAILED_CLIENT_ID || '',
    environment: process.env.NEXT_PUBLIC_NINETAILED_ENVIRONMENT || 'main',
    plugins: [
      // Any plugins that should always be loaded
    ],
    // Any other provider config
  };

  if (!enablePreview) {
    return function StandardNinetailedProvider({
      children,
    }: PropsWithChildren) {
      return (
        <NinetailedProvider {...providerProps}>{children}</NinetailedProvider>
      );
    };
  }

  return dynamic(() =>
    import('@ninetailed/experience.js-plugin-preview').then((mod) => {
      return function NinetailedProviderWithPreviewPlugin({
        children,
      }: PropsWithChildren) {
        providerProps.plugins = [
          new mod.NinetailedPreviewPlugin({
            audiences: ... // your audiences
            experiences: ... // your experiences
            ... // other config
          }),
          ...(providerProps.plugins || []),
        ];

        return (
          <NinetailedProvider {...providerProps}>{children}</NinetailedProvider>
        );
      };
    })
  );
}

Supplying Preview Content

The Preview Plugin expects a specific format for the provided experiences and audiences. Use the appropriate Utility Library for your content source to map these to the required format.

import { ExperienceMapper, ExperienceEntryLike, AudienceMapper, AudienceEntryLike } from 'experience.js-utils-contentful';
import { yourContentfulPreviewClient } from 'lib/api'

export const getAllExperiences = async () => {
  const entries = await yourContentfulPreviewClient.getEntries({
    content_type: 'nt_experience',
    include: 1,
  });
  return (entries.items as ExperienceEntryLike[])
    .filter(ExperienceMapper.isExperienceEntry)
    .map(ExperienceMapper.mapExperience);
};

export const getAllAudiences = async () => {
  const entries = await yourContentfulPreviewClient.getEntries({
    content_type: 'nt_audiences',
  });
  return (entries.items as AudienceEntryLike[])
    .filter(AudienceMapper.isAudienceEntry)
    .map(AudienceMapper.mapAudience);
};

Last updated