Displaying content

After you have successfully managed to display content from your CMS (based on one of the Connecting CMS guides) it's time to understand how exactly Shopstory SDK works.

No matter which CMS you're using, the pages/shopstory-block/[entryId].tsx page has an almost identical structure:

import type { NextPage, GetStaticProps, GetStaticPaths } from "next";
import {RenderableContent, Metadata, RawContent} from "@shopstory/core";
import { createClient, Entry } from "contentful";
import { ShopstoryClient } from "@shopstory/core";
import { Shopstory, ShopstoryMetadataProvider } from "@shopstory/react";
import { shopstoryConfig } from "../../src/shopstory/config";
import { DemoShopstoryProvider } from "../../src/shopstory/provider";

type ShopstoryBlockPageProps = {
  renderableContent: RenderableContent;
  meta: Metadata;
};

const ShopstoryBlockPage: NextPage<ShopstoryBlockPageProps> = (props) => {
  return (
    <DemoShopstoryProvider>
      <ShopstoryMetadataProvider meta={props.meta}>
        <Shopstory content={props.renderableContent} />
      </ShopstoryMetadataProvider>
    </DemoShopstoryProvider>
  );
};

export const getStaticPaths: GetStaticPaths = () => {
  return { paths: [], fallback: "blocking" };
};

export const getStaticProps: GetStaticProps<
  ShopstoryBlockPageProps,
  { entryId: string }
> = async (context) => {
  let { params, preview, locale = "en-US" } = context;

  if (!params) {
    return { notFound: true };
  }

  const rawContent = await fetchShopstoryContentJSONFromCMS(params.entryId, locale, !!preview);

  const shopstoryClient = new ShopstoryClient(shopstoryConfig, {
    locale,
    contentful: { preview }, // Contentful-specific piece of code
  });
  const renderableContent = shopstoryClient.add(rawContent);
  const meta = await shopstoryClient.build();

  return {
    props: { renderableContent, meta },
    revalidate: 10,
  };
};

async function fetchShopstoryContentJSONFromCMS(entryId: string, locale: string, preview: boolean) : RawContent {
  /**
    CMS specific operations that fetches the Shopstory JSON field data.
  **/
}

export default ShopstoryBlockPage;

Fetching data from the CMS

The visual content built with Shopstory is always stored in the CMS field so the first thing we must do is to fetch this data from the CMS. In our example this is done by fetchShopstoryContentJSONFromCMS function and it's the only piece of code that is CMS-specific. Each guide in Connecting CMS has a CMS-specific code example. The rest of the code works the same no matter what CMS is used.

Building content - ShopstoryClient

The content of the Shopstory field is called "raw content". Raw content is not ready to be sent to the browser. It's not optimised for rendering and 3rd party resources used in the content (media, other entries, products, etc) haven't been fetched yet. Raw content must go through the "build phase" in order to become ready to be sent to the browser and rendered. Content building is handled by ShopstoryClient.

Create ShopstoryClient instance

  const shopstoryClient = new ShopstoryClient(shopstoryConfig, { locale, contentful: { preview } });

Content building starts with creating ShopstoryClient instance. First parameter is the Shopstory configuration object. The second one are the "context parameters". locale is mandatory and should be compatible with locales available in your CMS.

Additionally, context parameters allows you to pass extra information to the Shopstory plugins. In this example (contentful: { preview }) we tell Contentful plugin whether Contentful assets linked by Shopstory content (assets or entries) should be fetched with Preview API or Content Delivery API. Each plugin has a different set of allowed properties.

Add the content

  const renderableContent = shopstoryClient.add(rawContent);

In this line we tell our Shopstory client that we want to build the rawContent JSON from the CMS. You can add as many content pieces as you need, in this example we add only one. The output of the add function is called Renderable Content. It's optimised for rendering and can be safely sent to the browser. It's very important to know that when you call add you only stage a content piece to be built. The result object is not "hydrated" until you call shopstoryClient.build(). Thanks to this mechanism you can add multiple Shopstory content pieces and then fetch resources for them in a single API call.

Build

  const meta = await shopstoryClient.build();

In this line we do the actual build. All the added content blocks are hydrated and all the resources are fetched.

The result of the build function is called "metadata". Metadata contains all the fetched resources and information about components used by the content. Both Metadata and Renderable Content objects are required to render the content in the browser.

Rendering the content

Rendering content is trivial:

const ShopstoryBlockPage: NextPage<ShopstoryBlockPageProps> = (props) => {
  return <DemoShopstoryProvider>
    <ShopstoryMetadataProvider meta={props.meta}>
      <Shopstory content={props.renderableContent} />
    </ShopstoryMetadataProvider>
  </DemoShopstoryProvider>
}

We simply pass Renderable Content object to the Shopstory component. Please remember that it must be wrapped with <ShopstoryMetadataProvider /> (which takes Metadata as a prop).

Server-side rendering

The last step is to configure SSR so that you don't experience FOUC (flash of unstyled content). Go to pages/_document.ts and use shopstoryGetStyleTag function:

import Document, {  Html, Head, Main, NextScript } from 'next/document'
import React from 'react'

import { shopstoryGetStyleTag } from '@shopstory/core/react'

class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>{shopstoryGetStyleTag()}</Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument

Summary

Congratulations! You made your first steps with Shopstory. You can already use it but in order to reap full benefits there's still some more configuration required.

Last updated