Skip to main content

Build new front-end

If you are doing a site redesign, you are creating your front-end from scratch. You do not need to worry about migrating your existing Sitecore front-end. Your focus is on creating front-end components that can access content from Sitecore items.

In addition, you may create new front-end components after you have finished migrating renderings & layouts from Sitecore and need to create new front-end components.

tip

This example uses Next.js as the front-end framework, but you can use any front-end technology you want. The layout and components defined in Canvas are technology agnostic.

Create web app

  1. Open a command-line interface (CLI).

  2. Enter the following commands:

    npx create-next-app headless-sitecore-site
    cd headless-sitecore-site
  3. Create a file named .env

  4. Add a file .env and define the following variables:

    NameValue
    UNIFORM_API_SITENAMEwebsite
    UNIFORM_API_KEYYour Uniform API key. Use the same value as you configured on the Canvas Integration Service in Sitecore.
    UNIFORM_PROJECT_IDYour Uniform Project ID. Use the same value as you configured on the Canvas Integration Service in Sitecore.

    The file will look like the following:

    UNIFORM_API_TOKEN=
    UNIFORM_PROJECT_ID=
    UNIFORM_API_URL=

Add npm packages

In order to add Uniform functionality to your web app, you must add a number of npm packages. Some of these packages are publically available and some require an npm access token.

  1. Set the following environment variable:
    • Name: NPM_TOKEN
    • Value: Your Uniform npm access token
  2. Add a file .npmrc:
    //registry.npmjs.org/:_authToken=${NPM_TOKEN}
  3. Enter the following commands:
    yarn add dotenv @uniformdev/canvas @uniformdev/canvas-react @uniformdev/cli
    yarn add @uniformdev/common-server @uniformdev/common-client @uniformdev/canvas-sitecore

Add Uniform config

You must configure Next.js to load Uniform settings when it runs.

  1. Open the file next.config.js.
  2. Change the contents to the following:
    const { uniformNextConfig } = require('@uniformdev/next-server');
    module.exports = uniformNextConfig();

Create front-end component

info

Coming soon.

Map component name to implementation

Each component defined in Canvas has a public ID. When you defined the component in Canvas, you specified this value.

When your web app reads composition definitions from Canvas, the composition and the components that are assigned to the composition are identified using this value.

You must provide instructions to your web app on how to map the public ID to the front-end implementation of that component. In your web app, React components provide that implementation.

Create a file lib/resolveRenderer.js:

import ManagedContent from '../components/ManagedContent';

const mapping = {};
mapping['managedContent'] = ManagedContent;

export const resolveRenderer = (component) => {
if (component?.type) {
const implementation = mapping[component.type]
if (implementation) return implementation;
}
return DefaultNotImplementedComponent
};

Add Uniform to page

In Canvas you have a composition that represents the home page and describes the layout for the page. You have a number of React components that represent the implementation of those components. Now you must configure the Next.js app to use the information from the Canvas composition in order to render the page using the React components you implemented.

Add composition component

Replace the contents of the file pages/index.jsx with the following:

import Head from 'next/head';
import { Composition, Slot } from "@uniformdev/canvas-react";
import { resolveRenderer } from "../lib/resolveRenderer";

export default function Home({ composition }) {
return (
<Composition data={composition} resolveRenderer={resolveRenderer}>
<>
<Head>
<title>{_name}</title>
</Head>
<div id="MainPanel">
<Slot name="main"></Slot>
</div>
</>
</Composition>
);
}
About this code

The component Composition accepts props that represent the composition (which is retrieved from Canvas) and a function that resolves the React component that corresponds to the public id for a component from Canvas.

This code also adds a component to render the slot main. You defined this slot when you defined the composition component.

Retrieve the composition

  1. Add the following code to the top of the file pages/index.jsx:

    import Head from 'next/head';
    import {
    CanvasClient,
    } from "@uniformdev/canvas";
    import { Composition, Slot } from "@uniformdev/canvas-react";
    import { resolveRenderer } from "../lib/resolveRenderer";

    async function getComposition(slug) {
    const client = new CanvasClient({
    apiKey: process.env.UNIFORM_API_KEY,
    projectId: process.env.UNIFORM_PROJECT_ID,
    });
    const { composition } = await client.getCompositionBySlug({
    slug,
    });
    return composition;
    }

    export default function Home({ composition }) {
    return (
    <Composition data={composition} resolveRenderer={resolveRenderer}>
    <>
    <Head>
    <title>{_name}</title>
    </Head>
    <div id="MainPanel">
    <Slot name="main"></Slot>
    </div>
    </>
    </Composition>
    );
    }
  2. Add the following code to the file pages/index.jsx:

    import Head from 'next/head';
    import {
    CanvasClient,
    } from "@uniformdev/canvas";
    import { Composition, Slot } from "@uniformdev/canvas-react";
    import { resolveRenderer } from "../lib/resolveRenderer";

    async function getComposition(slug) {
    const client = new CanvasClient({
    apiKey: process.env.UNIFORM_API_KEY,
    projectId: process.env.UNIFORM_PROJECT_ID,
    });
    const { composition } = await client.getCompositionBySlug({
    slug,
    });
    return composition;
    }

    export async function getStaticProps() {
    const slug = "/";
    const composition = await getComposition(slug);
    return {
    props: { composition },
    }
    }

    export default function Home({ composition }) {
    return (
    <Composition data={composition} resolveRenderer={resolveRenderer}>
    <>
    <Head>
    <title>{_name}</title>
    </Head>
    <div id="MainPanel">
    <Slot name="main"></Slot>
    </div>
    </>
    </Composition>
    );
    }

Add enhancer

When you retrieve the composition from Canvas, Sitecore item ids are included in the data. You must use an enhancer to retrieve the fields for those items.

info

Coming soon.

Enable live preview

Live preview enables you to view changes you make in Canvas almost immediately after you save them, without having to manually refresh the page.

  1. Create a file lib/useLivePreviewNextStaticProps.js:

    import { useRouter } from "next/router";
    import { useCallback } from "react";
    import { useCompositionEventEffect } from "@uniformdev/canvas-react";

    export function useLivePreviewNextStaticProps(options) {
    const router = useRouter();

    const effect = useCallback(() => {
    router.replace(router.asPath, undefined, { scroll: false });
    }, [router]);

    return useCompositionEventEffect({
    ...options,
    enabled: router.isPreview,
    effect,
    });
    }
  2. Add the following code to the file pages/index.jsx:

    import Head from 'next/head';
    import {
    CanvasClient,
    CANVAS_DRAFT_STATE,
    CANVAS_PUBLISHED_STATE,
    enhance,
    EnhancerBuilder,
    } from "@uniformdev/canvas";
    import { Composition, Slot } from "@uniformdev/canvas-react";
    import { getPageInfo, createItemEnhancer } from "@uniformdev/canvas-sitecore";
    import { noopLogger } from "@uniformdev/common-client";
    import { parseUniformServerConfig } from "@uniformdev/common-server";
    import { useLivePreviewNextStaticProps } from "../lib/useLivePreviewNextStaticProps";
    ...
  3. Make the following changes:

    async function getComposition(slug, state) {
    const client = new CanvasClient({
    apiKey: process.env.UNIFORM_API_KEY,
    projectId: process.env.UNIFORM_PROJECT_ID,
    });
    const { composition } = await client.getCompositionBySlug({
    slug,
    state,
    });
    return composition;
    }
  4. Make the following changes:

    export async function getStaticProps({ preview }) {
    const slug = "/";
    const state = preview ? CANVAS_DRAFT_STATE : CANVAS_PUBLISHED_STATE;
    const composition = await getComposition(slug, state);
    const config = parseUniformServerConfig(process.env, noopLogger, true);
    const { pageId, pageData } = await getPageInfo({
    composition,
    config,
    isPreview: preview
    });
    const itemEnhancer = createItemEnhancer({
    pageId,
    pageItem: pageData,
    config,
    isPreview: preview,
    throwOnNotFound: true,
    });

    const enhancers = new EnhancerBuilder().component(
    [
    "managedContent",
    ],
    (builder) => builder.data("model", itemEnhancer)
    );
    await enhance({ composition, enhancers });
    return {
    props: { composition },
    };
    }
  5. Make the following changes:

    export default function Home({ composition }) {
    useLivePreviewNextStaticProps({
    compositionId: composition?._id,
    projectId: process.env.NEXT_PUBLIC_UNIFORM_PROJECT_ID,
    });
    return (
    <Composition data={composition} resolveRenderer={resolveRenderer}>
    <>
    <Head>
    <title>{_name}</title>
    </Head>
    <div id="MainPanel">
    <Slot name="main"></Slot>
    </div>
    </>
    </Composition>
    );
    }
  6. In your .env file, add the following variable and set its value so it matches UNIFORM_PROJECT_ID:

    NEXT_PUBLIC_UNIFORM_PROJECT_ID=

Start the web app

When you run the web app, you should see the page from the default Sitecore site, but Sitecore isn't handling page rendering. Sitecore is only acting as a headless CMS.

  1. In the terminal, enter the following command:

    yarn dev
  2. Open your browser to http://localhost:3000