UI BuilderUI Builder
Core
  • Introduction
  • Quick Start
Component System
  • Components Intro
  • Shadcn Registry
  • Custom Components
  • Advanced Configuration
  • Panel Configuration
Data & Variables
  • Variables
  • Variable Binding
  • Function Registry
  • Editing Restrictions
Layout & Persistence
  • Layer Structure
  • State Management & Persistence
Rendering
  • Rendering Pages
  • Page Type Renderers
  1. Rendering
  2. Page Type Renderers
Page Type Renderers

UI Builder supports multiple page types via the pageTypeRenderers and pageTypeCodeGenerators props. This extension point lets you add support for email templates, PDFs, React Native, or any other rendering target — without modifying UI Builder's internals.

UI Builder stays framework-agnostic. All framework-specific logic lives in your consumer code.

Loading...
Loading...
Try it now

Available React Email Components

The react-email registry includes definitions for all commonly used @react-email/components:

ComponentNotes
HtmlRoot element — new email pages default to this
HeadEmail <head> — must be inside Html
PreviewInbox preview text — must be inside Html
BodyEmail body — inside Html or Tailwind
TailwindApplies Tailwind CSS to email — wraps Body
ContainerCentered max-width table container
SectionSemantic content section
RowTable row layout
ColumnTable cell — must be inside Row
TextParagraph / text content
ButtonCTA link-button with VML Outlook support
LinkAnchor link
ImgEmail-safe image
HrHorizontal divider

Tailwind in Email

React email supports a subset of Tailwind via the <Tailwind> wrapper component. Wrap your <Body> with <Tailwind> and use DEFAULT_EMAIL_TAILWIND_CONFIG so pixelBasedPreset is applied by default.

The react-email registry also uses email-specific className field overrides, so class suggestions avoid shadcn-only/theme-variable classes that do not map cleanly to email clients.

Supported: Most layout utilities (flex, grid, p-*, m-*, text-*, bg-*, etc.)
Not supported: prose (requires @tailwindcss/typography), space-*, hover: pseudo-class, most media queries in older clients.

See caniemail.com for client support details.

Loading...

API Reference

pageTypeRenderers?: PageTypeRenderers

A map of page type key → PageTypeRenderer config. When provided, UI Builder shows a page type selector in the "Create new page" popover and dispatches canvas rendering to the matching renderer for pages of that type.

ts
1interface PageTypeRenderer { 2 /** Display label shown in the page type selector (e.g. "Email", "PDF") */ 3 label?: string; 4 /** Root component type for new pages of this type. Defaults to "div". */ 5 defaultRootLayerType?: string; 6 /** Initial props for the root component of a new page. */ 7 defaultRootLayerProps?: Record<string, unknown>; 8 /** 9 * When true, EditorPanel skips AutoFrame + ResizableWrapper entirely. 10 * Reserved for page types that CANNOT render inside an iframe (PDF, react-native-web). 11 * Do NOT set for email — email renders fine inside AutoFrame. 12 */ 13 skipAutoFrame?: boolean; 14 /** 15 * Filters the global componentRegistry shown in the add-component popover. 16 * Return a subset of the registry to restrict available components. 17 */ 18 filterRegistry?: (registry: ComponentRegistry) => ComponentRegistry; 19 /** Renders the editor canvas for this page type. */ 20 renderEditorCanvas: (props: PageTypeRendererProps) => React.ReactNode; 21}

pageTypeCodeGenerators?: PageTypeCodeGenerators

A map of page type key → PageTypeCodeGenerator. When present for the active page, replaces the default "React" code tab in the code panel.

ts
1interface PageTypeCodeGenerator { 2 /** Generates the code/output string for the page. */ 3 generateCode: (page: ComponentLayer, registry: ComponentRegistry) => string; 4 /** Tab label, e.g. "HTML Email", "PDF", "React Native". Defaults to "React". */ 5 label?: string; 6}

Email Builder Example

The react-email registry ships everything needed to wire up UI Builder as a full email editor — component definitions, canvas substitutes, code generator, and page renderer config. UI Builder itself has zero knowledge of @react-email/*.

1. Install react-email components registry

bash
1npx shadcn@latest add https://raw.githubusercontent.com/olliethedev/ui-builder/main/registry/react-email-components-registry.json

This installs:

  • lib/ui-builder/registry/react-email-component-definitions.ts — ComponentRegistry for all 14 react-email components
  • lib/ui-builder/email/email-builder-utils.tsx — ready-to-use emailPageRenderer, emailCodeGenerator, DEFAULT_EMAIL_TAILWIND_CONFIG, and canvas substitutes
  • npm packages: @react-email/components, @react-email/render, web-streams-polyfill

2. Wire up the email builder

Import and pass the pre-built config objects to UIBuilder:

tsx
1import UIBuilder from '@/components/ui/ui-builder'; 2import { reactEmailComponentDefinitions } from '@/lib/ui-builder/registry/react-email-component-definitions'; 3import { 4 emailPageRenderer, 5 emailCodeGenerator, 6 DEFAULT_EMAIL_TAILWIND_CONFIG, 7} from '@/lib/ui-builder/email/email-builder-utils'; 8 9export function EmailBuilder() { 10 return ( 11 <UIBuilder 12 componentRegistry={reactEmailComponentDefinitions} 13 pageTypeRenderers={{ email: emailPageRenderer }} 14 pageTypeCodeGenerators={{ email: emailCodeGenerator }} 15 tailwindThemePanel={false} 16 initialLayers={[ 17 { 18 id: 'email-1', 19 type: 'Html', 20 name: 'Email', 21 pageType: 'email', 22 props: { lang: 'en' }, 23 children: [ 24 { id: 'email-head-1', type: 'Head', name: 'Head', props: {}, children: [] }, 25 { id: 'email-preview-1', type: 'Preview', name: 'Preview', props: {}, children: 'Preview text' }, 26 { 27 id: 'email-tailwind-1', 28 type: 'Tailwind', 29 name: 'Tailwind', 30 props: { config: DEFAULT_EMAIL_TAILWIND_CONFIG }, 31 children: [ 32 { 33 id: 'email-body-1', 34 type: 'Body', 35 name: 'Body', 36 props: { className: 'bg-white font-sans' }, 37 children: [], 38 }, 39 ], 40 }, 41 ], 42 }, 43 ]} 44 allowPagesCreation 45 /> 46 ); 47}

3. Safari / iOS compatibility

Add this import at the top of your email route's entry file for Safari/iOS compatibility with @react-email/render:

ts
1import 'web-streams-polyfill/polyfill';

Customizing the Email Builder

The email-builder-utils.tsx file also exports the lower-level building blocks if you need to customize behavior — for example, to modify how structural HTML elements are substituted on the canvas:

tsx
1import { useMemo } from 'react'; 2import LayerRenderer from '@/components/ui/ui-builder/layer-renderer'; 3import { 4 NullComponent, 5 CanvasHtml, 6 CanvasBody, 7 generateEmailCode, 8} from '@/lib/ui-builder/email/email-builder-utils'; 9import type { 10 PageTypeRenderer, 11 PageTypeRendererProps, 12 ComponentRegistry, 13} from '@/components/ui/ui-builder/types'; 14 15// Custom canvas renderer — add your own substitutes or logic 16function MyEmailCanvasRenderer({ page, componentRegistry, editorConfig }: PageTypeRendererProps) { 17 const canvasRegistry: ComponentRegistry = useMemo(() => { 18 const base = { ...componentRegistry }; 19 if (base.Html) base.Html = { ...base.Html, component: CanvasHtml }; 20 if (base.Head) base.Head = { ...base.Head, component: NullComponent }; 21 if (base.Preview) base.Preview = { ...base.Preview, component: NullComponent }; 22 if (base.Body) base.Body = { ...base.Body, component: CanvasBody }; 23 return base; 24 }, [componentRegistry]); 25 26 return ( 27 <LayerRenderer 28 className="contents" 29 page={page} 30 componentRegistry={canvasRegistry} 31 editorConfig={editorConfig} 32 /> 33 ); 34} 35 36const myEmailPageRenderer: PageTypeRenderer = { 37 label: 'Email', 38 defaultRootLayerType: 'Html', 39 defaultRootLayerProps: { lang: 'en' }, 40 renderEditorCanvas: (props) => <MyEmailCanvasRenderer {...props} />, 41};

Extending to Other Targets

The PageTypeRenderer / PageTypeCodeGenerator pattern is designed to support any rendering target:

react-pdf

tsx
1const pdfPageRenderer: PageTypeRenderer = { 2 label: 'PDF', 3 defaultRootLayerType: 'Document', 4 skipAutoFrame: true, // PDF viewer cannot render inside an iframe 5 filterRegistry: (registry) => pdfOnlyRegistry, 6 renderEditorCanvas: (props) => ( 7 <PDFViewer style={{ width: '100%', height: '100%' }}> 8 <LayerRenderer {...props} /> 9 </PDFViewer> 10 ), 11}; 12 13const pdfCodeGenerator: PageTypeCodeGenerator = { 14 label: 'PDF', 15 generateCode: (page, registry) => { 16 // Generate React PDF JSX 17 return `// PDF template for ${page.name}`; 18 }, 19};

Future: react-native-web

tsx
1const nativePageRenderer: PageTypeRenderer = { 2 label: 'Native', 3 filterRegistry: (registry) => nativeComponentRegistry, 4 renderEditorCanvas: (props) => <LayerRenderer {...props} />, 5};