Loading...
Loading...
Integrates the Unlayer editor into web applications — React, Vue, Angular, or plain JavaScript setup, component props, editor access patterns, multiple instances.
npx skill4agent add unlayer/unlayer-skills unlayer-integration| Framework | Package | Install | Editor Access |
|---|---|---|---|
| React | | | |
| Vue | | | |
| Angular | | | |
| Plain JS | CDN script tag | | Global |
⚠️ Before installing any Unlayer package, verify the version exists on npm:bashnpm view react-email-editor version # check latest published versionNever pin a version number you haven't verified. Usewithout a version to get the latest, or runnpm install <package> --saveto see all available versions.npm view <package> versions --json
npm install react-email-editor --saveimport React, { useRef, useState } from 'react';
import EmailEditor, { EditorRef, EmailEditorProps } from 'react-email-editor';
const EmailBuilder = () => {
const emailEditorRef = useRef<EditorRef>(null);
const [saving, setSaving] = useState(false);
// Save design JSON + export HTML to your backend
const handleSave = () => {
const unlayer = emailEditorRef.current?.editor;
if (!unlayer) return;
setSaving(true);
unlayer.exportHtml(async (data) => {
try {
const response = await fetch('/api/templates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
design: data.design, // Save this — needed to edit later
html: data.html, // The rendered HTML output
}),
});
if (!response.ok) throw new Error('Save failed');
console.log('Saved successfully');
} catch (err) {
console.error('Save error:', err);
} finally {
setSaving(false);
}
});
};
// Load a saved design when editor is ready
const onReady: EmailEditorProps['onReady'] = async (unlayer) => {
try {
const response = await fetch('/api/templates/123');
if (response.ok) {
const saved = await response.json();
unlayer.loadDesign(saved.design); // Pass the saved design JSON
}
} catch (err) {
console.log('No saved design, starting blank');
}
};
return (
<div>
<button onClick={handleSave} disabled={saving}>
{saving ? 'Saving...' : 'Save'}
</button>
<EmailEditor
ref={emailEditorRef}
onReady={onReady}
options={{
projectId: 123456, // Dashboard > Project > Settings
displayMode: 'email',
}}
/>
</div>
);
};
export default EmailBuilder;// POST /api/templates — save
{ design: object, html: string }
// GET /api/templates/:id — load
{ design: object, html: string, updatedAt: string }| Prop | Type | Default | Description |
|---|---|---|---|
| | | All |
| | | Per-tool configuration |
| | | Theme and panel settings |
| | — | Called when editor is ready (receives |
| | — | Called when iframe loads (before ready) |
| | | Container inline styles |
| | | Minimum editor height |
npm install vue-email-editor --save<template>
<div id="app">
<button v-on:click="handleSave" :disabled="saving">
{{ saving ? 'Saving...' : 'Save' }}
</button>
<EmailEditor ref="emailEditor" v-on:load="editorLoaded" />
</div>
</template>
<script>
import { EmailEditor } from 'vue-email-editor';
export default {
components: { EmailEditor },
data() {
return { saving: false };
},
methods: {
async editorLoaded() {
try {
const response = await fetch('/api/templates/123');
if (response.ok) {
const saved = await response.json();
this.$refs.emailEditor.editor.loadDesign(saved.design);
}
} catch (err) {
console.log('No saved design, starting blank');
}
},
handleSave() {
this.saving = true;
this.$refs.emailEditor.editor.exportHtml(async (data) => {
try {
await fetch('/api/templates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ design: data.design, html: data.html }),
});
} catch (err) {
console.error('Save error:', err);
} finally {
this.saving = false;
}
});
},
},
};
</script>minHeightoptionstoolsappearancelocaleprojectIdnpm install angular-email-editor --saveimport { EmailEditorModule } from 'angular-email-editor';
@NgModule({ imports: [EmailEditorModule] })
export class AppModule {}import { Component, ViewChild } from '@angular/core';
import { EmailEditorComponent } from 'angular-email-editor';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
@ViewChild(EmailEditorComponent)
private emailEditor: EmailEditorComponent;
saving = false;
async editorLoaded() {
try {
const response = await fetch('/api/templates/123');
if (response.ok) {
const saved = await response.json();
this.emailEditor.editor.loadDesign(saved.design);
}
} catch (err) {
console.log('No saved design');
}
}
handleSave() {
this.saving = true;
this.emailEditor.editor.exportHtml(async (data) => {
try {
await fetch('/api/templates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ design: data.design, html: data.html }),
});
} catch (err) {
console.error('Save error:', err);
} finally {
this.saving = false;
}
});
}
}<div>
<button (click)="handleSave()" [disabled]="saving">
{{ saving ? 'Saving...' : 'Save' }}
</button>
<email-editor (loaded)="editorLoaded($event)"></email-editor>
</div><script src="https://editor.unlayer.com/embed.js"></script>
<div id="editor-container" style="height: 700px;"></div>
<script>
unlayer.init({
id: 'editor-container',
projectId: 1234,
displayMode: 'email',
});
unlayer.addEventListener('editor:ready', async function () {
try {
const response = await fetch('/api/templates/123');
if (response.ok) {
const saved = await response.json();
unlayer.loadDesign(saved.design);
}
} catch (err) {
console.log('Starting blank');
}
});
function handleSave() {
unlayer.exportHtml(async function (data) {
await fetch('/api/templates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ design: data.design, html: data.html }),
});
});
}
</script>createEditor()init()const editor1 = unlayer.createEditor({ id: 'email-editor', displayMode: 'email' });
const editor2 = unlayer.createEditor({ id: 'web-editor', displayMode: 'web' });
editor1.loadDesign(emailDesign);
editor2.loadDesign(webDesign);| Mistake | Fix |
|---|---|
| Container too small | Minimum 1024px wide x 700px tall. Editor fills its container. |
| Calling methods before ready | Always wait for |
Using | Use |
Missing | Get it from Dashboard > Project > Settings |
| Only saving HTML, not design JSON | Always save both — design JSON lets users edit later |
| No loading state while saving | Disable the save button to prevent double saves |
| Pinning a non-existent package version | Run |
| Error | Cause | Fix |
|---|---|---|
| Editor shows blank white space | Container div has 0 height | Set explicit height: |
| Script not loaded or wrong | Check |
| Accessed before mount | Only use inside |
| Design doesn't load | Malformed JSON | Validate JSON, check |