Skip to content

Use React or Next.js#

Use this guide to show a Storyteller view in a React or Next.js app. Initialize the SDK once for the page, create the view after the component mounts, and destroy the view when the component unmounts.

Install the SDK with npm first.

Initialize the SDK once#

Add a module that calls initialize once and shares its promise with every component:

// storyteller.js
import * as Storyteller from '@getstoryteller/storyteller-sdk-javascript';

let initialization;

export function startStoryteller() {
  if (!initialization) {
    initialization = Storyteller.sharedInstance
      .initialize('demo-api-key')
      .catch((error) => {
        initialization = undefined;
        throw error;
      });
  }

  return initialization;
}

The first call starts initialize. Later calls return the same promise. If initialize fails, the helper clears the cached promise, so the next call tries again.

Each initialize call resets the global theme (Storyteller.sharedInstance.theme) to the defaults. If you use a global theme, set it after initialize resolves, for example in a .then() inside startStoryteller(). A theme set in a view's configuration is kept. See Customize themes.

When the signed-in user changes, call initialize again as described in Identify and personalize users, then set the global theme again.

React#

Import the SDK, its stylesheet, and the helper:

import { useEffect } from 'react';
import * as Storyteller from '@getstoryteller/storyteller-sdk-javascript';
import '@getstoryteller/storyteller-sdk-javascript/dist/storyteller.min.css';
import { startStoryteller } from './storyteller';

Create the view after startStoryteller() resolves, and destroy it in the effect cleanup:

export function StoryRow() {
  useEffect(() => {
    let isMounted = true;
    let storyRow;

    startStoryteller()
      .then(() => {
        if (isMounted) {
          storyRow = new Storyteller.StorytellerStoriesRowView(
            'storyteller-stories-row'
          );
        }
      })
      .catch((error) => console.error('Storyteller could not start.', error));

    return () => {
      isMounted = false;
      storyRow?.destroy();
    };
  }, []);

  return <div id="storyteller-stories-row" style={{ height: 200 }} />;
}

In development, React Strict Mode runs each effect, its cleanup, and the effect again. The cleanup sets isMounted to false, so only the second effect creates a view. Both effects get the same promise from startStoryteller(), so the SDK initializes once.

Each view needs a container ID that is unique on the page. If you render the component more than once at the same time, pass a different ID to each instance. The ID must follow the container ID rules.

Next.js App Router#

Import the stylesheet in app/layout.js.

import '@getstoryteller/storyteller-sdk-javascript/dist/storyteller.min.css';

Keep the view in a Client Component so the SDK runs in the browser. Add 'use client' at the top of the component file, then use the same StoryRow component as in the React section:

'use client';

import { useEffect } from 'react';
import * as Storyteller from '@getstoryteller/storyteller-sdk-javascript';
import { startStoryteller } from './storyteller';

The Storyteller Web Showcase uses the same lifecycle. Its isStorytellerInitialized guard runs before view creation. Cleanup calls view.destroy().

Next steps#