# Activate visual editing

> Use the Uniform SDKs with Next.js or Nuxt 3 to activate the Canvas visual editor in Uniform.

Source: https://docs.uniform.app/docs/guides/composition/visual-editing/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](#nextjs) and [Nuxt 3](#nuxt-3).

## Enable preview support in code

> **Prerequisite:**
>
> These instructions assume you already have enabled rendering of Canvas compositions in your front-end application. For instructions, see the [rendering](https://docs.uniform.app/docs/guides/composition/rendering#render-composition) 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](https://docs.uniform.app/docs/guides/composition/visual-editing/troubleshoot-preview) if you have any issues with enabling preview mode.

## Next.js

> **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](https://docs.uniform.app/docs/learn/tutorials/nextjs-app-router#2-2-configure-the-preview-handler).

### Add preview route handler

After you have [activated composition rendering](https://docs.uniform.app/docs/guides/composition/rendering#render-composition) 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 variable | Description |
   | --- | --- |
   | `UNIFORM_PREVIEW_SECRET` | This can be any value you want. However, this value must be included in the preview URL that you will configure in a later step. |

   > **Note:**
   >
   > 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:

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

   > **Note:**
   >
   > This adds a handler to redirect preview requests to the right page.
   > You can read more about Preview Mode in [Next.js docs](https://nextjs.org/docs/advanced-features/preview-mode).

### Customize preview route handler

The previous code snippet works for simple projects that have basic routing
and don't need [enhancers](https://docs.uniform.app/docs/guides/composition/enhancers).
For more advanced projects you can customize the preview route handler to support enhancers and custom routing.

#### Enabling enhancers in preview mode

If your project uses [enhancers](https://docs.uniform.app/docs/guides/composition/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.

```js
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
```

#### Custom routing in preview mode

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.

```js
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
```

## Nuxt 3

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`

```html
<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

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](https://docs.uniform.app/docs/guides/composition/visual-editing/activate-visual-editing/inline-editing) to learn how to enable inline editing in your components.

## Placeholders for empty slots

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

![A placeholder for an empty slot](https://docs.uniform.app/images/guides/composition/contextual-editing/placeholder.png)

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.

### Customize placeholders

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.

**React/Next.js**

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

**Vue/Nuxt**

```html
<UniformSlot name="mySlotName">
  <template #emptyPlaceholder>
    <div :style="{width: '100px', height: '100px'}" />
  </template>
</UniformSlot>
```

### Disable placeholders

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

**React/Next.js**

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

**Vue/Nuxt**

```html
<UniformSlot name="mySlotName">
  <template #emptyPlaceholder />
</UniformSlot>
```

## Visual editing for compositions

> **Warning:**
>
> Visual editing requires [preview mode](https://docs.uniform.app/docs/guides/composition/visual-editing/preview-mode) be activated first.

**Next.js**

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.

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

**Nuxt 3**

Visual editing is enabled by default when using [`<UniformComposition />`](https://sdk.uniform.app/uniform-nuxt) to render your compositions.
If you're using `useUniformComposition`, the enhancer will be automatically inherited by `<UniformComposition />`.
Otherwise, you'll need to pass it manually via the `contextualEditingEnhancer` prop.

**Next.js with enhancers**

Visual editing is enabled by default when using `<UniformComposition />` to render your compositions. If you're using [enhancers](https://docs.uniform.app/docs/guides/composition/enhancers), make sure you pass the enhancing function to the [`contextualEditingEnhancer`](https://sdk.uniform.app/canvas-react.uniformcomposition) prop.

```jsx
import {
  UniformComposition,
  createUniformApiEnhancer,
} from "@uniformdev/canvas-react";
export default function PageComposition({ preview, data: composition }) {
  // create enhancer function using preview mode
  const contextualEditingEnhancer = createUniformApiEnhancer({
    apiUrl: "/api/preview",
  });
  return (
    <div>
      <UniformComposition
        data={composition}
        // enable automatic enhancing for contextual editing
        contextualEditingEnhancer={contextualEditingEnhancer}
      >
        {/* Render additional slots here */}
      </UniformComposition>
    </div>
  );
}
```

## Visual editing for patterns

Unlike compositions, [** component patterns**](https://docs.uniform.app/docs/guides/patterns/component-patterns) or [** composition patterns**](https://docs.uniform.app/docs/guides/patterns/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.

**Next.js**

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.

   ```tsx
   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`.

   ```tsx
   import { createPreviewHandler } from '@uniformdev/canvas-next';

   const handler = createPreviewHandler({
     playgroundPath: '/playground',
     // ...otherAttributes
   });

   export default handler;
   ```

### Decorators

> **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](https://sdk.uniform.app/canvas-react.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:

```tsx
import type { UniformPlaygroundDecorator } from '@uniformdev/canvas-react';

export const MyDecorator: UniformPlaygroundDecorator = ({ children, data }) => {
  return (
    <div>
      <h2>My Decorator 🎉</h2>
      {children}
    </div>
  );
};
```

Decorator usage:

```tsx
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.](https://docs.uniform.app/images/guides/composition/contextual-editing/container-resizer-decorator.mp4)

> **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.

```tsx
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 }}
    />
  );
};
```

**Nuxt 3**

1. Create a new route for the preview playground, for example: `pages/playground.vue`.
2. Add <UniformPlayground /> to the page.

   ```html
   <script setup>
   import { resolveRenderer } from '../components';
   </script>
   <template>
     <UniformPlayground :resolve-renderer="resolveRenderer" />
   </template>
   ```

   > **Info:**
   >
   > `resolveRenderer` is a function that should return the Vue components to render for each Uniform component.
   > It should include all the possible components.
3. In `nuxt.config.ts` add the `playgroundPath` attribute to Uniform module's config.

   ```ts
   export default defineNuxtConfig({
     uniform: {
       playgroundPath: '/playground',
       // ...otherAttributes
     },
   });
   ```

**Next.js with enhancers**

You can use custom enhancers within the playground by implementing the following code:

```tsx
import {
  UniformPlayground,
  createUniformApiEnhancer,
} from "@uniformdev/canvas-react";
import '../components';

export default function Page() {
  // create enhancer function using preview mode
  const contextualEditingEnhancer = createUniformApiEnhancer({
    apiUrl: "/api/preview",
  });

  return (
    <UniformPlayground
      // enable automatic enhancing for contextual editing
      contextualEditingEnhancer={contextualEditingEnhancer}
    />
  );
}
```

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