Activate visual editing

Uniform Canvas offers a visual editing workspace, which facilitates making structural changes directly from within the preview panel. This is available for both compositions and patterns.

Uniform provides SDKs for visual editing for Next.js and Nuxt 3.

Prerequisite

These instructions assume you already have enabled rendering of Canvas compositions in your front-end application. For instructions, see the rendering section.

How you activate visual editing in your application depends on the technology used to build the front end.

Troubles enabling preview?

Consult the troubleshooting guide if you have any issues with enabling preview mode.

info

These instructions apply to Next.js projects using the Pages Router. If you're using the App Router instead, please refer to our Next.js App Router tutorial.

After you have activated composition rendering in your front-end application, you need to add a preview route.

A number of new files are needed to support visual editing.

  1. Start your front-end application

  2. Add the following values to your .env file:

    Environment variableDescription
    UNIFORM_PREVIEW_SECRETThis can be any value you want. However, this value must be included in the preview URL that you will configure in a later step.

    About this step

    Technically you don't need to set the preview secret in an environment variable. You could hard-code the value into your app, but using environment variables is a "best practice."

  3. Install dependencies

    npm install @uniformdev/canvas-next
  4. Add the following preview handler:

    import { createPreviewHandler } from '@uniformdev/canvas-next' const handler = createPreviewHandler({ secret: () => process.env.UNIFORM_PREVIEW_SECRET, }) export default handler

    About this step

    This adds a handler to redirect preview requests to the right page. You can read more about Preview Mode in Next.js docs.

The previous code snippet works for simple projects that have basic routing and don't need enhancers. For more advanced projects you can customize the preview route handler to support enhancers and custom routing.

If your project uses enhancers, the following changes need to be added to the preview route handler to ensure that the enhancers are executed in preview mode when the composition is updated in Canvas.

import { createPreviewHandler } from '@uniformdev/canvas-next' // import custom enhancer function - change this import to match to your project setup import runEnhancers from "@/lib/enhancers"; // pass preview context to enhancers const context = { preview: true }; const handler = createPreviewHandler({ secret: () => process.env.UNIFORM_PREVIEW_SECRET, // run enhancers when content is updated in Canvas enhance: (composition) => runEnhancers(composition, context) }) export default handler

In some cases you might want to have a different URL for your composition previews. By specifying a custom resolveFullPath function you can construct your own URL that will be used for the preview mode.

import { createPreviewHandler } from '@uniformdev/canvas-next' // Example: render every composition in a localized "/preview/" route const handler = createPreviewHandler({ secret: () => process.env.UNIFORM_PREVIEW_SECRET, // add custom preview routing logic resolveFullPath: ({ slug, path, id, locale }) => { return `/preview/${locale}${path}`; }, }) export default handler

The Uniform module for Nuxt integrates preview mode out of the box. No front-end application modifications are needed. But you still need to configure the Uniform project in order for the preview button to work.

pages/[...slug].vue

<script lang="ts" setup> // access the currently active locale from the preview context const { locale } = useNuxtApp().$preview ?? {}; // Do something with the locale. </script>

Inline editing allows content editors to make changes to the text of a component directly in the preview panel.

Refer to the inline editing guide to learn how to enable inline editing in your components.

When a composition has an empty slot, you'll see a placeholder as shown in the screenshot below:

A placeholder for an empty slot
A placeholder for an empty slot

These placeholders help content editors be aware of the slot's existence and have a visual way to add a component to the empty slot directly in the preview panel. Each placeholder can be dismissed by clicking on the X icon. This preference is saved in the browser's local storage, so it will not be shown again for that slot on the same composition.

To match the dimensions of a real components, the placeholder is based on one of the allowed components in that specific slot.

But it might happen that the placeholder doesn't look exactly as expected. In this case you can control the dimensions of the placeholder by rendering a custom component.

<UniformSlot name="mySlotName" emptyPlaceholder={<div style={{ width: "100px", height: "100px" }}></div>} />

If you don't want to show placeholders for a specific slot of a component then you can disable them:

<UniformSlot name="mySlotName" emptyPlaceholder={null} />

warning

Visual editing requires preview mode be activated first.

Visual editing is enabled by default when using <UniformComposition /> to render your compositions. See the Next.js with enhancers tab for requirements for that use case.

import { UniformComposition } from "@uniformdev/canvas-react"; export default function PageComposition({ data }) { return ( <UniformComposition data={data}> {/* Render additional slots here */} </UniformComposition> ); }

Unlike compositions, component patterns or composition patterns don't have a project map node assigned. This means that the application doesn't know which route in your front-end app can preview a pattern.

To make this possible, you need to build a page dedicated to previewing patterns. You'll create a new route in your front-end application (for example, /playground), add <UniformPlayground /> to that page, and then let the SDK know where to redirect the pattern preview requests, using the playgroundPath attribute. See the framework-specific instructions below to learn how to achieve that.

  1. Create a new route for the preview playground, for example: pages/playground.tsx or app/playground/page.tsx.

  2. Add <UniformPlayground /> to the page.

    import { UniformPlayground } from '@uniformdev/canvas-react'; import '../components'; export default function Page() { return <UniformPlayground /> }

    info

    ../components is used as an example here, but you need to import a file that registers all the possible components.

  3. In /api/preview.tsx add the playgroundPath attribute to createPreviewHandler.

    import { createPreviewHandler } from '@uniformdev/canvas-next'; const handler = createPreviewHandler({ playgroundPath: '/playground', // ...otherAttributes }); export default handler;

warning

This is an experimental feature. Subject to breaking changes in future releases.

Patterns are designed for use inside other compositions, but an isolated preview of the pattern might not give an accurate representation of how the pattern will look inside a composition. To improve this, Uniform has introduced a concept of "decorators," which wrap patterns with additional rendering functionality.
Decorators are typically used to stand in for the composition where the pattern will be used, or to add some rendering controls (re-sizer, background color changer, etc.).

To create a decorator, you can use the type UniformPlaygroundDecorator for guidance. The decorator component receives 2 props:

  • children (ReactNode) the rendered pattern, with any prior decorators applied to it.
  • data (RootComponentInstance) the data of the pattern. For instance, this can be used to render a specific mock per component type.

Decorator definition:

import type { UniformPlaygroundDecorator } from '@uniformdev/canvas-react'; export const MyDecorator: UniformPlaygroundDecorator = ({ children, data }) => { return ( <div> <h2>My Decorator 🎉</h2> {children} </div> ); };

Decorator usage:

import '../components'; import { MyDecorator } from '../playgroundDecorators/MyDecorator'; import { UniformPlayground } from '@uniformdev/canvas-react'; const PlaygroundPage = () => { return <UniformPlayground decorators={[MyDecorator]} />; }; export { PlaygroundPage as default };

Examples

Resize the container
Demo of the container resizer decorator.

tip

To allow interacting with dynamic decorators even when Page Interactions are disabled, you need to add IS_RENDERED_BY_UNIFORM_ATTRIBUTE to the clickable elements.

import { IS_RENDERED_BY_UNIFORM_ATTRIBUTE } from '@uniformdev/canvas'; import type { UniformPlaygroundDecorator } from '@uniformdev/canvas-react'; import { useState } from 'react'; const widthOptions = [720, 512, 420, 360]; export const ContainerResizer: UniformPlaygroundDecorator = ({ children }) => { const [selectedWidth, setSelectedWidth] = useState<number>(); return ( <div> <div style={{ height: 24, position: 'relative', justifyContent: 'flex-end' }}> <ContainerResizerBar width={undefined} onSelect={setSelectedWidth} /> {widthOptions.map((width) => ( <ContainerResizerBar key={width} width={width} onSelect={setSelectedWidth} /> ))} </div> <div style={{ width: selectedWidth, margin: 'auto' }}>{children}</div> </div> ); }; const ContainerResizerBar = ({ width, onSelect, }: { width: number | undefined; onSelect: (width: number | undefined) => void; }) => { return ( <button key={width} style={{ width: width ?? '100%', height: '100%', position: 'absolute', top: 0, left: '50%', transform: 'translateX(-50%)', background: '#ddd', border: '2px solid #999', }} onClick={() => onSelect(width)} {...{ [IS_RENDERED_BY_UNIFORM_ATTRIBUTE]: true }} /> ); };