Rendering

This section provides information on how to programmatically interact with the process used to render components and compositions defined using Uniform Canvas.

When a Canvas component has slots, it's expected that the components in the slot will be rendered in the front-end application. Each Canvas component must be mapped to a component type that's compatible with the front-end technology used. You must provide the mapping for Uniform.

This example demonstrates how to define the mapping and assign it to the component that controls the rendering process.

import { registerUniformComponent } from "@uniformdev/canvas-react"; export default function HeroComponent() { return <div>Hero Component Content</div> } registerUniformComponent({ type: 'hero', component: HeroComponent, });

As <UniformSlot /> component doesn't import FE components directly, we need to import them manually here to include them to the bundle and run our registerUniformComponent()

import './HeroComponent'; // Optionally you can override default fallback component - DefaultNotImplementedComponent // Useful when your component mapping is not simple 1-to-1 relation import { registerUniformComponent, NOT_IMPLEMENTED_COMPONENT } from "@uniformdev/canvas-react"; const MyNotImplementedComponent = (props) => { return ( <div>COMPONENT {<code>{props.component.componentType}</code>} NOT IMPLEMENTED</div> ); } registerUniformComponent({ type: NOT_IMPLEMENTED_COMPONENT, component: MyNotImplementedComponent, });

You still need to import canvasComponents.js to include all the components, such as in NextJS _app.js is the best place for it.

import '../components/canvasComponents';
import Head from 'next/head' import { CanvasClient } from '@uniformdev/canvas' import { UniformComposition, UniformSlot, DefaultNotImplementedComponent } from '@uniformdev/canvas-react' export async function getStaticProps() { const client = new CanvasClient({ apiKey: process.env.UNIFORM_API_KEY, projectId: process.env.UNIFORM_PROJECT_ID, }) const { composition } = await client.getCompositionBySlug({ slug: "/", }) return { props: { title: "Example page", composition, } } } function resolveRenderer({ type }) { const components = { // 'my_type': MyComponent, }; return components[type] ?? DefaultNotImplementedComponent; } export default function Home({ title, composition }) { return ( <UniformComposition data={composition} resolveRenderer={resolveRenderer}> <div> <Head> <title>{title}</title> </Head> <main> <UniformSlot name='body' /> </main> </div> </UniformComposition> ) }

info

For more information, see the UniformComposition component reference.

During the composition rendering process, Uniform generates markup. By default, the markup is surrounded by a <code>div</code> tag. This tag can cause problems with certain forms of CSS.

This example demonstrates how to prevent this extra markup from being generated.

import Head from 'next/head' import { UniformComposition, UniformSlot } from '@uniformdev/canvas-react' function resolveRenderer(component) { ... } export default function Home({ composition }) { return ( <UniformComposition data={composition} resolveRenderer={resolveRenderer} behaviorTracking="onLoad"> <div> <Head> <title>Example</title> </Head> <main> <UniformSlot name='body' /> </main> </div> </UniformComposition> ) }

A slot lets you define a list of child components to render inside a <UniformSlot /> component.

By default, an array of children renders inside a minimal container (in case of React, it's React.Fragment).

React

function Slide({ title, description, imageUrl }) { return ( <div className="slide"> <img src={imageUrl}/> <h4>{title}</h4> <p>{description}</p> </div> ) }; function Slider({ title }) { return ( <div className="slider"> <h3>{title}</h3> <div className="slides"> <UniformSlot name='slides' /> </div> </div> ) }

The final React output will look like this:

React

<div class="slider"> <h3>Slider title</h3> <div className="slides"> <> <div class="slide"> <img src="image-url-1"/> <h4>Slide 1</h4> <p>Slide 1 description</p> </div> <div class="slide"> <img src="image-url-2"/> <h4>Slide 2</h4> <p>Slide 2 description</p> </div> </> </div> </div>

Sometimes you may want to control how a slot renders its children. For example, the popular slider library Swiper requires you to wrap slides with <SwiperSlide /> component. Each slide should be the immediate child of the parent <Swiper /> component. To achieve this in Uniform you can use the wrapperComponent prop of the <UniformSlot /> component.

React

import { Swiper, SwiperSlide } from 'swiper/react'; function Slide({ title, description, imageUrl }) { return ( <> <img src={imageUrl}/> <h4>{title}</h4> <p>{description}</p> </> ) }; function SliderWrapperComponent({ items }) { return ( <Swiper> {items.map((item, index) => ( <SwiperSlide key={index}> {item} </SwiperSlide> ))} </Swiper> ) } function Slider({ title }) { return ( <div className="slider"> <h3>{title}</h3> <div className="slides"> <UniformSlot name='slides' wrapperComponent={SliderWrapperComponent} /> </div> </div> ) }

info

For more information, see the UniformComposition component reference, and the guide for routing with Next.js.