Quickstart
Empty Node project to a rendered HTML string, in three steps.
1. Install
npm install @unlayer/react-elements react react-dom
If your project doesn't have a tsconfig.json yet, create one with the modern JSX runtime. The .tsx samples below assume it, and won't compile without:
{
"compilerOptions": {
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "bundler",
"target": "ES2020",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
See Installation for peer dependencies and tsconfig notes.
2. Build Your First Email
Create welcome-email.tsx:
import {
Email,
Row,
Column,
ColumnLayouts,
Heading,
Paragraph,
Button,
} from '@unlayer/react-elements';
export function WelcomeEmail() {
return (
<Email backgroundColor="#f0f0f0" contentWidth="600px">
<Row
layout={ColumnLayouts.OneColumn}
backgroundColor="#ffffff"
padding="20px"
>
<Column>
<Heading
headingType="h1"
fontSize="24px"
fontFamily={{ label: 'Arial', value: 'arial,helvetica,sans-serif' }}
>
Welcome!
</Heading>
<Paragraph fontSize="14px">Thanks for signing up.</Paragraph>
<Button
href="https://example.com"
backgroundColor="#0879A1"
color="#ffffff"
>
Get Started
</Button>
</Column>
</Row>
</Email>
);
}
The structure is always wrapper → Row → Column → content. Layout Components explains why.
3. Render to HTML
Create render.tsx:
import { renderToHtml } from '@unlayer/react-elements';
import { WelcomeEmail } from './welcome-email';
const html = renderToHtml(<WelcomeEmail />);
console.log(html);
Run it:
npx tsx render.tsx
A complete email-safe HTML document prints to stdout. Pipe it to a file, send it through your SMTP provider, hand it to a PDF engine. It's a plain string.
💡 For real email sending, prefer
renderToHtmlPartsoverrenderToHtml. It returns{ head, body }separately, so the<style>block (hover effects, responsive media queries) lands in<head>where email clients expect it. See Rendering API.
A footgun worth knowing about
If you pass a config override to renderToHtml, the override is silently dropped:
// Looks fine. Does nothing.
renderToHtml(<WelcomeEmail />, { cdnBaseUrl: '...' });
That's because the config lands on WelcomeEmail, which doesn't forward it. Either render the wrapper directly:
renderToHtml(<Email>...</Email>, { cdnBaseUrl: '...' });
Or have WelcomeEmail accept and forward config to <Email>. Configuration covers both shapes.
Two-Column Variant
Switch the layout to TwoEqual and add a second <Column>:
<Row layout={ColumnLayouts.TwoEqual} backgroundColor="#ffffff" padding="20px">
<Column>
<Heading
headingType="h1"
fontSize="24px"
fontFamily={{ label: 'Arial', value: 'arial,helvetica,sans-serif' }}
>
Hello World
</Heading>
</Column>
<Column>
<Paragraph html="Welcome to our newsletter!" fontSize="14px" />
</Column>
</Row>
The column count must match the layout. TwoEqual wants exactly two columns; mismatch throws at validation time. See Limitations for the other common ways to trip up.
What's Next
- Wrappers — switch from
EmailtoPageorDocument, or drop down toBody. - Configuration — merge tags, CDN base URL, text direction.
- Rendering API —
renderToHtml,renderToHtmlParts,renderToPlainText,renderToJson,renderRowToJson.