Activate composition
You have configured the instructions that control composition. Next you need to update the web app to execute those instructions. This involves the following:
- Understanding how the configuration from Canvas is used in a web app.
- Updating a web application so that a layout is read from Canvas.
Overview
You configured a composition that defines layout instructions for the home page. The next step is to update the front-end application so it uses those layout instructions.
Add npm packages
Open a terminal in the root of the repository.
Enter the following commands:
- Next.js
- Nuxt 3
cd examples/docs/intro-to-canvas/nextjs/no-uniform
npm install @uniformdev/canvas @uniformdev/canvas-reactAbout this stepThis adds references to the packages for React apps that need to use Canvas.
cd examples/docs/intro-to-canvas/nuxtjs/no-uniform
npm install @uniformdev/canvas @uniformdev/context @uniformdev/canvas-vue @uniformdev/context-vue @uniformdev/uniform-nuxtcautionIf you get an error while installing these packages you might need to add the
--legacy-peer-deps
switch to the end of the command above.About this stepThis adds references to the packages for Nuxt 3 apps that need to use Canvas.
Add enhancer
When you created your composition and added a component to the Body slot in the last step, you specified a content id for the component. That content ID is stored by Uniform, and the front-end application must retrieve the details for the item. To simplify that process, Uniform provides an "enhancer" API that calls the data source to fetch those details and collect them into a single object for use by front-end developers.

Add the following file to the root of your application:
import { enhance, EnhancerBuilder } from "@uniformdev/canvas";
import content from "../content/content.json";
// Uses the parameter value from the composition
// to look up the topic from the data file. If
// the topic is found, the fields are returned.
const dataEnhancer = async ({ component }) => {
const contentId = component?.parameters?.contentId?.value;
if (contentId) {
const topic = content.find((e) => e.id == contentId);
if (topic) {
return { ...topic.fields };
}
}
};
export default async function doEnhance(composition) {
const enhancedComposition = { ...composition };
const enhancers = new EnhancerBuilder().data("fields", dataEnhancer);
await enhance({
composition: enhancedComposition,
enhancers,
});
return enhancedComposition;
}
Register your components
- Next.js
- Nuxt 3
Your React Component must be registered within Uniform registry:
import { registerUniformComponent } from "@uniformdev/canvas-react";
export default function Body(props) {
return <p>{props.text}</p>
}
registerUniformComponent({
type: 'body',
component: Body,
});
For more information, see the Register your component section.
A component resolver is needed to map the Uniform component name to React components. This means you must add the mapping to you front-end application.
Add the following file:
import Body from "../components/Body.vue";
import { DefaultNotImplementedComponent } from "@uniformdev/canvas-vue";
export default function resolveRenderer({ type }) {
if (type == "defaultBody") {
return Body;
}
return DefaultNotImplementedComponent;
}
Add layout component for Canvas
- Next.js
- Nuxt 3
Add the following file:
import Head from "next/head";
import { UniformSlot } from "@uniformdev/canvas-react";
import Footer from "./Footer";
export default function LayoutCanvas({ title }) {
return (
<div className="container">
<Head>
<title>{title}</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<UniformSlot name="body" />
<Footer />
</div>
);
}
Add the following file:
<script lang="ts" setup>
import Footer from "./Footer.vue";
defineProps<{ title: string }>();
</script>
<template>
<div class="container">
<Head>
<Title>{{ title }}</Title>
<link rel="icon" href="/favicon.ico" />
</Head>
<UniformSlot name="body" />
<Footer />
</div>
</template>
Add Canvas to the home page
Adding Canvas to the home page involves making changes to the front-end application. Follow the steps that match your front-end technology.
Next.js
- Steps
- Complete
This section guides you through the process of activating Canvas by explaining each step. It takes longer to go through, but it will help you understand why each line of code is needed.
Edit the following file:
/pages/index.jsimport Layout from "../src/components/Layout";
import content from "../content/content.json";
async function getComposition(slug) {
}
export async function getStaticProps() {
const slug = "/";
const topic = content.find((e) => e.url == slug);
return { props: { fields: topic.fields } };
}
export default function Home({ fields }) {
return <Layout content={content} fields={fields} />;
}About this stepThis adds a function that will hold the logic needed to retrieve the composition from Uniform.
Edit the following file:
/pages/index.jsimport { CanvasClient } from "@uniformdev/canvas";
import Layout from "../src/components/Layout";
import content from "../content/content.json";
async function getComposition(slug) {
const client = new CanvasClient({
apiKey: process.env.UNIFORM_API_KEY,
projectId: process.env.UNIFORM_PROJECT_ID,
});
}
export async function getStaticProps() {
const slug = "/";
const topic = content.find((e) => e.url == slug);
return { props: { fields: topic.fields } };
}
export default function Home({ fields }) {
return <Layout content={content} fields={fields} />;
}About this stepThis adds the client object used to make API calls to Uniform.
Edit the following file:
/pages/index.jsimport { CanvasClient } from "@uniformdev/canvas";
import Layout from "../src/components/Layout";
import content from "../content/content.json";
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 topic = content.find((e) => e.url == slug);
return { props: { fields: topic.fields } };
}
export default function Home({ fields }) {
return <Layout content={content} fields={fields} />;
}About this stepThis retrieves the composition from Uniform and returns it.
Edit the following file:
/pages/index.jsimport { CanvasClient } from "@uniformdev/canvas";
import Layout from "../src/components/Layout";
import content from "../content/content.json";
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 topic = content.find((e) => e.url == slug);
const composition = await getComposition(slug);
return { props: { fields: topic.fields } };
}
export default function Home({ fields }) {
return <Layout content={content} fields={fields} />;
}About this stepThis adds a call to the function that retrieves the composition from Uniform.
Edit the following file:
/pages/index.jsimport { CanvasClient } from "@uniformdev/canvas";
import Layout from "../src/components/Layout";
import content from "../content/content.json";
import doEnhance from "../lib/enhancer";
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 topic = content.find((e) => e.url == slug);
const composition = await getComposition(slug);
await doEnhance(composition);
return { props: { fields: topic.fields } };
}
export default function Home({ fields }) {
return <Layout content={content} fields={fields} />;
}About this stepThis runs the enhancer, which adds data to the composition.
Edit the following file:
/pages/index.jsimport { CanvasClient } from "@uniformdev/canvas";
import Layout from "../src/components/Layout";
import content from "../content/content.json";
import doEnhance from "../lib/enhancer";
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 topic = content.find((e) => e.url == slug);
const composition = await getComposition(slug);
await doEnhance(composition);
//
//Return props for the home page that
//include the composition and content
//required by the page components.
return {
props: {
composition,
fields: topic.fields,
},
};
}
export default function Home({ fields }) {
return <Layout content={content} fields={fields} />;
}About this stepThis returns the props.
Edit the following file:
/pages/index.jsimport { CanvasClient } from "@uniformdev/canvas";
import Layout from "../src/components/Layout";
import content from "../content/content.json";
import doEnhance from "../lib/enhancer";
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 topic = content.find((e) => e.url == slug);
const composition = await getComposition(slug);
await doEnhance(composition);
//
//Return props for the home page that
//include the composition and content
//required by the page components.
return {
props: {
composition,
fields: topic.fields,
},
};
}
export default function Home({ composition, fields }) {
return <Layout content={content} fields={fields} />;
}About this stepThis makes the composition available to the React component.
Edit the following file:
/pages/index.jsimport { CanvasClient } from "@uniformdev/canvas";
import { UniformComposition } from "@uniformdev/canvas-react";
import Layout from "../src/components/Layout";
import content from "../content/content.json";
import doEnhance from "../lib/enhancer";
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 topic = content.find((e) => e.url == slug);
const composition = await getComposition(slug);
await doEnhance(composition);
//
//Return props for the home page that
//include the composition and content
//required by the page components.
return {
props: {
composition,
fields: topic.fields,
},
};
}
export default function Home({ composition, fields }) {
return (
<UniformComposition data={composition} resolveRenderer={resolveRenderer}>
<Layout content={content} fields={fields} />
</UniformComposition>
);
}About this stepThis adds the Uniform component that handles composition tasks in the app.
Edit the following file:
/pages/index.jsimport { CanvasClient } from "@uniformdev/canvas";
import { UniformComposition } from "@uniformdev/canvas-react";
import LayoutCanvas from "../src/components/LayoutCanvas";
import content from "../content/content.json";
import doEnhance from "../lib/enhancer";
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 topic = content.find((e) => e.url == slug);
const composition = await getComposition(slug);
await doEnhance(composition);
//
//Return props for the home page that
//include the composition and content
//required by the page components.
return {
props: {
composition,
fields: topic.fields,
},
};
}
export default function Home({ composition, fields }) {
return (
<UniformComposition data={composition} resolveRenderer={resolveRenderer}>
<LayoutCanvas composition={composition} fields={fields} />
</UniformComposition>
);
}About this stepThis replaces the default layout component with one that is Canvas-aware.
This section gets you to activating canvas as quickly as possible. It does not explain each line of code.
Edit the following file:
import { CanvasClient } from "@uniformdev/canvas";
import { UniformComposition } from "@uniformdev/canvas-react";
import LayoutCanvas from "../src/components/LayoutCanvas";
import content from "../content/content.json";
import doEnhance from "../lib/enhancer";
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 topic = content.find((e) => e.url == slug);
const composition = await getComposition(slug);
await doEnhance(composition);
//
//Return props for the home page that
//include the composition and content
//required by the page components.
return {
props: {
composition,
fields: topic.fields,
},
};
}
export default function Home({ composition, fields }) {
return (
<UniformComposition data={composition} resolveRenderer={resolveRenderer}>
<LayoutCanvas composition={composition} fields={fields} />
</UniformComposition>
);
}
This is the completed page file.
Nuxt 3
- Steps
- Complete
This section guides you through the process of activating Canvas by explaining each step. It takes longer to go through, but it will help you understand why each line of code is needed.
Edit the following file:
/nuxt.config.tsimport { defineNuxtConfig } from "nuxt";
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
css: ["~/styles/globals.css", "~/styles/page.css"],
modules: ["@uniformdev/uniform-nuxt"],
});About this stepThis adds the Uniform module, which brings Uniform functionality to Nuxt.
Edit the following file:
/nuxt.config.tsimport { defineNuxtConfig } from "nuxt";
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
css: ["~/styles/globals.css", "~/styles/page.css"],
modules: ["@uniformdev/uniform-nuxt"],
uniform: {
projectId: process.env.UNIFORM_PROJECT_ID,
readOnlyApiKey: process.env.UNIFORM_API_KEY,
apiHost: process.env.UNIFORM_CLI_BASE_URL,
},
});About this stepThis sets the values the Uniform modules uses to connect to Uniform. You set the project ID and API key values as environment variables in a previous step. The API host value is optional, and is used when you want to use an alternate endpoint for the Uniform API.
Edit the following file:
/nuxt.config.tsimport { defineNuxtConfig } from "nuxt";
import { ManifestV2 } from "@uniformdev/context";
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
css: ["~/styles/globals.css", "~/styles/page.css"],
modules: ["@uniformdev/uniform-nuxt"],
uniform: {
projectId: process.env.UNIFORM_PROJECT_ID,
readOnlyApiKey: process.env.UNIFORM_API_KEY,
apiHost: process.env.UNIFORM_CLI_BASE_URL,
manifest: {} as ManifestV2,
},
});About this stepThe Nuxt module for Uniform requires a manifest be set in order to pass validation checks within the Nuxt module. This sets a manifest in order to meet this requirement.
Edit the following file:
/nuxt.config.tsimport { defineNuxtConfig } from "nuxt";
import { ManifestV2 } from "@uniformdev/context";
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
css: ["~/styles/globals.css", "~/styles/page.css"],
modules: ["@uniformdev/uniform-nuxt"],
uniform: {
projectId: process.env.UNIFORM_PROJECT_ID,
readOnlyApiKey: process.env.UNIFORM_API_KEY,
apiHost: process.env.UNIFORM_CLI_BASE_URL,
manifest: {} as ManifestV2,
defaultConsent: true,
},
});About this stepThe Nuxt module for Uniform requires a manifest be set in order to pass validation checks within the Nuxt module. This sets a manifest in order to meet this requirement.
Edit the following file:
/pages/index.vue<script lang="ts" setup>
import content from "../content/content.json";
const slug = "/";
const topic = content.find((e) => e.url == slug);
const { composition } = await useUniformComposition({ slug });
</script>
<template>
<Layout :content="content" :fields="topic.fields" />
</template>About this stepThis uses the Uniform module to retrieve the composition using the specified slug.
Edit the following file:
/pages/index.vue<script lang="ts" setup>
import content from "../content/content.json";
import doEnhance from "../lib/enhancer";
const slug = "/";
const topic = content.find((e) => e.url == slug);
const { composition } = await useUniformComposition({
slug,
enhance: async (c) => await doEnhance(c)
});
</script>
<template>
<Layout :content="content" :fields="topic.fields" />
</template>About this stepThis applies the enhancer to the composition.
Edit the following file:
/pages/index.vue<script lang="ts" setup>
import content from "../content/content.json";
import doEnhance from "../lib/enhancer";
import resolveRenderer from "../lib/resolveRenderer";
const slug = "/";
const topic = content.find((e) => e.url == slug);
const { composition } = await useUniformComposition({
slug,
enhance: async (c) => await doEnhance(c)
});
</script>
<template>
<UniformComposition
v-if="composition"
:data="composition"
:resolve-renderer="resolveRenderer"
>
<Layout :content="content" :fields="topic.fields" />
</UniformComposition>
</template>About this stepIn Canvas you added a component to a slot in a composition. The front-end application must determine which front-end component to use to render the Canvas component. The component you add in this step makes this decision.
Edit the following file:
/pages/index.vue<script lang="ts" setup>
import content from "../content/content.json";
import doEnhance from "../lib/enhancer";
import resolveRenderer from "../lib/resolveRenderer";
import LayoutCanvas from "../components/LayoutCanvas.vue";
const slug = "/";
const topic = content.find((e) => e.url == slug);
const { composition } = await useUniformComposition({
slug,
enhance: async (c) => await doEnhance(c)
});
</script>
<template>
<UniformComposition
v-if="composition"
:data="composition"
:resolve-renderer="resolveRenderer"
>
<LayoutCanvas :title="topic.fields.title" />
</UniformComposition>
</template>About this stepThis adds the layout component you created earlier.
This section gets you to activating canvas as quickly as possible. It does not explain each line of code.
Edit the following file:
/nuxt.config.tsimport { defineNuxtConfig } from "nuxt";
import { ManifestV2 } from "@uniformdev/context";
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
css: ["~/styles/globals.css", "~/styles/page.css"],
modules: ["@uniformdev/uniform-nuxt"],
uniform: {
projectId: process.env.UNIFORM_PROJECT_ID,
readOnlyApiKey: process.env.UNIFORM_API_KEY,
apiHost: process.env.UNIFORM_CLI_BASE_URL,
manifest: {} as ManifestV2,
defaultConsent: true,
},
});Edit the following file:
/pages/index.vue<script lang="ts" setup>
import content from "../content/content.json";
import doEnhance from "../lib/enhancer";
import resolveRenderer from "../lib/resolveRenderer";
import LayoutCanvas from "../components/LayoutCanvas.vue";
const slug = "/";
const topic = content.find((e) => e.url == slug);
const { composition } = await useUniformComposition({
slug,
enhance: async (c) => await doEnhance(c)
});
</script>
<template>
<UniformComposition
v-if="composition"
:data="composition"
:resolve-renderer="resolveRenderer"
>
<LayoutCanvas :title="topic.fields.title" />
</UniformComposition>
</template>About this stepThis is the completed page file.