Skip to main content

Drag-and-Drop Example

The @react-email-dnd example app (packages/editor/example) showcases how to wire the editor into a production-ready shell. The Vanilla route is a minimal drag-and-drop configuration that uses the shared Canvas document format, custom blocks, and live change callbacks.

packages/editor/example/src/Vanilla.tsx
import { CanvasDocument, CanvasProvider, EmailEditor } from '@react-email-dnd';
import { customBlocks } from './components/custom-blocks';
import { advancedFonts as fonts } from './font-examples';

const testDocument: CanvasDocument = {
version: 1,
meta: { title: 'Font Rendering Test' },
variables: {},
theme: {
fonts: [
{
id: 'honk',
fontFamily: 'Honk',
fallbackFontFamily: 'system-ui, sans-serif',
webFont: {
url: 'https://fonts.gstatic.com/s/honk/v6/m8J7jftUea-XwTaemClumrBQbmvynOmXBji9zFhHRr8WFgV7pLFMWJEav63D.woff2',
format: 'woff2',
},
fontWeight: 400,
fontStyle: 'normal',
},
],
},
sections: [
{
id: 'section-1',
type: 'section',
rows: [
{
id: 'row-1',
type: 'row',
gutter: 16,
padding: '6',
columns: [
{
id: 'column-1',
type: 'column',
padding: '4',
blocks: [
{
id: 'heading-1',
type: 'heading',
props: {
content: 'Most professional mail',
as: 'h1',
fontFamily: 'Honk',
},
},
{
id: 'text-1',
type: 'text',
props: {
content: 'This is a very professional mail',
fontFamily: 'Honk',
},
},
{
id: 'button-1',
type: 'button',
props: {
label: 'Honk Font Button',
backgroundColor: '#2563eb',
fontFamily: 'Honk',
},
},
],
},
],
},
],
},
],
};

export const Vanilla = () => {
return (
<CanvasProvider
initialDocument={testDocument}
onSave={() => console.log('save')}
onDocumentChange={(document) => console.log('updated document', document)}
>
<EmailEditor customBlocks={customBlocks} fonts={fonts} showHidden />
</CanvasProvider>
);
};

Key pieces to replicate in your own host application:

  • CanvasProvider wraps the editor with undo/redo history, change events, and persistence helpers.
  • EmailEditor renders the palette, canvas, and property panels. Pass your customBlocks registry and fonts array to keep the editor aligned with renderer capabilities.
  • Initial document seeds the canvas so users see an existing email instead of a blank slate.

Custom block registry

The example exposes a HeroBanner custom block that combines layout primitives from @react-email/components. It registers both the rendered output and the property editor form in one definition:

packages/editor/example/src/components/custom-blocks.tsx
export const heroBlockDefinition: CustomBlockDefinition<HeroBlockProps> = {
id: 'hero-banner',
type: 'custom',
label: 'Hero Banner',
icon: SparkleIcon,
defaults: {
componentName: 'HeroBlock',
props: {
title: 'Launch something delightful',
description:
'Announce a promotion, product update, or new feature with a bold hero.',
ctaLabel: 'Shop now',
ctaHref: 'https://example.com',
backgroundColor: '#0ea5e9',
textColor: '#ffffff',
},
},
component: HeroBlock,
propEditor: HeroBlockPropsEditor,
};

The HeroBlockPropsEditor implements a controlled form that pipes changes back into the editor JSON. Preview output is rendered directly in the canvas, so authors get instant feedback while adjusting settings.

Running the example locally

cd packages/editor/example
pnpm install
pnpm dev

The dev server exposes /daisyui and /vanilla routes that you can adapt for your own design system and typography stacks.