A Svelte 5 wrapper for the Unlayer email editor - drag & drop email template builder.
Demo: https://unlayer-svelte.iamdani.sh/
This repository is organized as a monorepo with two workspaces:
unlayer-svelte/
- The main SDK package (@unlayer/svelte
)example/
- Example application demonstrating SDK usage
# Install dependencies for all workspaces
npm install
# Run the example app
npm run dev
# Build the SDK package
npm run build
# Build all workspaces
npm run build:all
npm install @unlayer/svelte
<script lang="ts">
import { UnlayerEditor } from '@unlayer/svelte';
import type { UnlayerEditorOptions } from '@unlayer/svelte';
let editorRef: any;
let isLoaded = false;
const handleReady = (editor: any) => {
isLoaded = true;
console.log('Editor is ready!', editor);
};
const exportHtml = () => {
if (editorRef && isLoaded) {
editorRef.exportHtml((data: any) => {
console.log('Exported HTML:', data.html);
console.log('Design JSON:', data.design);
});
}
};
const editorOptions: UnlayerEditorOptions = {
minHeight: '500px'
};
</script>
<UnlayerEditor
bind:this={editorRef}
options={{
...editorOptions,
onReady: handleReady
}}
/>
<button on:click={exportHtml} disabled={!isLoaded}>
Export HTML
</button>
- Type:
object | null
- Default:
null
- Description: Initial design to load in the editor
- Type:
Record<string, any>
- Default:
{}
- Description: Custom tools configuration
- Type:
UnlayerEditorOptions
- Default:
{}
- Description: Editor configuration options
The options
prop accepts an UnlayerEditorOptions
object with the following properties:
- Type:
string
- Default:
'100%'
- Description: Minimum height of the editor
- Type:
(editor: UnlayerEditor) => void
- Description: Callback function called when the editor is ready
- Type:
(editor: UnlayerEditor) => void
- Description: Callback function called when the editor is loaded
- Type:
string
- Description: Custom ID for the editor instance
- Type:
string
- Description: Custom URL for the Unlayer script
- Type:
object
- Description: Additional Unlayer editor configuration options
- Type:
Record<string, any>
- Description: Custom tools configuration
The component exposes the following methods via template ref:
Load a design into the editor.
<script>
let editorRef;
const loadSampleDesign = () => {
const sampleDesign = { /* your design object */ };
editorRef.loadDesign(sampleDesign);
};
</script>
<UnlayerEditor bind:this={editorRef} />
Export the current design as HTML.
<script>
let editorRef;
const exportDesign = () => {
editorRef.exportHtml((data) => {
console.log('HTML:', data.html);
console.log('Design:', data.design);
});
};
</script>
Show the preview mode.
<script>
const showPreview = () => {
editorRef.showPreview('desktop');
};
</script>
Hide the preview mode.
<script>
const hidePreview = () => {
editorRef.hidePreview();
};
</script>
<script lang="ts">
import { UnlayerEditor } from '@unlayer/svelte';
import type { UnlayerEditorOptions } from '@unlayer/svelte';
let editorRef: any;
let isLoaded = false;
let htmlOutput = '';
let previewMode = false;
const handleReady = (editor: any) => {
isLoaded = true;
console.log('Editor ready!', editor);
};
const exportHtml = () => {
if (editorRef && isLoaded) {
editorRef.exportHtml((data: any) => {
htmlOutput = data.html;
});
}
};
const togglePreview = () => {
if (previewMode) {
editorRef.hidePreview();
} else {
editorRef.showPreview('desktop');
}
previewMode = !previewMode;
};
const loadSampleDesign = () => {
const sampleDesign = {
body: {
rows: [
{
cells: [1],
columns: [
{
contents: [
{
type: 'text',
values: {
text: 'Hello World!'
}
}
]
}
]
}
]
}
};
editorRef.loadDesign(sampleDesign);
};
const editorOptions: UnlayerEditorOptions = {
minHeight: '600px',
onReady: handleReady
};
</script>
<div class="editor-container">
<div class="toolbar">
<button on:click={loadSampleDesign} disabled={!isLoaded}>
Load Sample
</button>
<button on:click={togglePreview} disabled={!isLoaded}>
{previewMode ? 'Hide' : 'Show'} Preview
</button>
<button on:click={exportHtml} disabled={!isLoaded}>
Export HTML
</button>
</div>
<UnlayerEditor
bind:this={editorRef}
options={editorOptions}
/>
{#if htmlOutput}
<div class="html-output">
<h3>Exported HTML:</h3>
<pre>{htmlOutput}</pre>
</div>
{/if}
</div>
<style>
.editor-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.toolbar {
margin-bottom: 20px;
display: flex;
gap: 10px;
}
.toolbar button {
padding: 8px 16px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.toolbar button:disabled {
background: #ccc;
cursor: not-allowed;
}
.html-output {
margin-top: 20px;
padding: 20px;
background: #f5f5f5;
border-radius: 4px;
}
.html-output pre {
max-height: 300px;
overflow-y: auto;
background: white;
padding: 10px;
border-radius: 4px;
}
</style>
- Svelte 5.0.0 or higher
- Modern browser with ES2015+ support
This project uses npm workspaces. All commands should be run from the root directory:
# Install dependencies for all workspaces
npm install
# Run the example app (develops against local SDK)
npm run dev
# Build the SDK package
npm run build
# Run tests for the SDK
npm run test
# Lint all workspaces
npm run lint
# Format all workspaces
npm run format
# Type check all workspaces
npm run check
The SDK package can be published independently:
# Build and publish the SDK
npm run publish:sdk
# Update SDK version
npm run version:sdk patch|minor|major
# Run prepack for SDK
npm run prepack:sdk
You can also work with individual workspaces:
# Work in the SDK workspace
cd unlayer-svelte/
npm run dev
# Work in the example workspace
cd example/
npm run dev
The example app in example/
demonstrates how to use the SDK and serves as a development environment. It automatically uses the local SDK via the workspace dependency.
Apache-2.0
Contributions are welcome! Please feel free to submit a Pull Request.
If you encounter any issues, please file them on the GitHub issue tracker.