How to Save Templates
This guide explains how to save email templates to your backend database or storage system.
Overview​
Saving templates in Easy Email Pro is handled through the onSubmit callback function. This function is called when the user explicitly triggers a save action (typically through a save button).
Basic Setup​
Step 1: Configure onSubmit Handler​
Define the onSubmit handler when creating the editor config:
Editor.tsx
import { EmailEditorProvider, EmailTemplate } from "easy-email-pro-editor";
import { Retro, ThemeConfigProps } from "easy-email-pro-theme";
import { EditorCore } from "easy-email-pro-core";
import mjml from "mjml-browser";
import axios from "axios";
const onSubmit: ThemeConfigProps["onSubmit"] = async (values, editor) => {
try {
// Convert template to MJML and HTML
const mjmlStr = EditorCore.toMJML({
element: values.content,
mode: "production",
beautify: true,
});
const html = mjml(mjmlStr).html;
// Save to your backend
await axios.post("/api/templates", {
subject: values.subject,
content: values.content,
html: html,
mjml: mjmlStr,
});
console.log("Template saved successfully");
} catch (error) {
console.error("Failed to save template:", error);
}
};
const config = Retro.useCreateConfig({
onSubmit: onSubmit,
// ... other config
});
Step 2: Trigger Save from UI​
Use the submit function from useEditorContext to trigger the save:
EditorHeader.tsx
import { useEditorContext } from "easy-email-pro-theme";
import { Button, Message } from "@arco-design/web-react";
function EditorHeader() {
// useEditorContext can only be called within EmailEditorProvider
const { submit, dirty } = useEditorContext();
const handleSave = async () => {
try {
await submit();
Message.success("Template saved successfully");
} catch (error) {
Message.error("Failed to save template");
}
};
return (
<Button
type="primary"
disabled={!dirty}
onClick={handleSave}
>
Save Template
</Button>
);
}
Understanding the Save Flow​
- User clicks Save: The save button calls
submit() - onSubmit is triggered: Your
onSubmithandler receives the current template values - Process template: Convert to desired formats (MJML, HTML, etc.)
- Save to backend: Send data to your API
- Handle response: Show success/error messages
Save Options​
Option 1: Save JSON Only​
Save the template data structure as JSON:
const onSubmit = async (values: EmailTemplate) => {
await axios.post("/api/templates", {
subject: values.subject,
content: values.content,
});
};
Option 2: Save with HTML​
Save both JSON and generated HTML:
const onSubmit = async (values: EmailTemplate) => {
const mjmlStr = EditorCore.toMJML({
element: values.content,
mode: "production",
});
const html = mjml(mjmlStr).html;
await axios.post("/api/templates", {
subject: values.subject,
content: values.content,
html: html,
});
};
Option 3: Save with All Formats​
Save JSON, MJML, and HTML:
const onSubmit = async (values: EmailTemplate) => {
const mjmlStr = EditorCore.toMJML({
element: values.content,
mode: "production",
beautify: true,
});
const html = mjml(mjmlStr).html;
await axios.post("/api/templates", {
subject: values.subject,
content: values.content, // JSON format
mjml: mjmlStr, // MJML format
html: html, // Final HTML
});
};
Tracking Changes​
Use the dirty flag to indicate if the template has unsaved changes:
const { submit, dirty } = useEditorContext();
// Show indicator when there are unsaved changes
{dirty && <Badge>Unsaved changes</Badge>}
// Disable save button when nothing has changed
<Button disabled={!dirty} onClick={() => submit()}>
Save
</Button>
Auto-Save (Optional)​
You can implement auto-save using the onChange callback:
const onChange = async (values: EmailTemplate) => {
// Auto-save every 30 seconds
debouncedAutoSave(values);
};
const debouncedAutoSave = debounce(async (values: EmailTemplate) => {
await saveTemplate(values);
}, 30000);
Complete Example​
For a complete implementation example, check our live demo code.
import { useEditorContext } from "easy-email-pro-theme";
import { EditorCore } from "easy-email-pro-core";
import mjml from "mjml-browser";
import { Button, Message, Space } from "@arco-design/web-react";
import { useState } from "react";
function SaveButton() {
const { submit, dirty } = useEditorContext();
const [saving, setSaving] = useState(false);
const handleSave = async () => {
setSaving(true);
try {
await submit();
Message.success("Saved successfully");
} catch (error) {
Message.error("Save failed");
} finally {
setSaving(false);
}
};
return (
<Space>
{dirty && <span>Unsaved changes</span>}
<Button
type="primary"
disabled={!dirty}
onClick={handleSave}
loading={saving}
>
Save Template
</Button>
</Space>
);
}
Notes​
useEditorContextcan only be called within components wrapped byEmailEditorProvider- The
dirtyflag tracks if the template has been modified since last save - Always handle errors in your
onSubmithandler - Consider adding loading states during save operations