Skip to main content

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​

  1. User clicks Save: The save button calls submit()
  2. onSubmit is triggered: Your onSubmit handler receives the current template values
  3. Process template: Convert to desired formats (MJML, HTML, etc.)
  4. Save to backend: Send data to your API
  5. 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​

  • useEditorContext can only be called within components wrapped by EmailEditorProvider
  • The dirty flag tracks if the template has been modified since last save
  • Always handle errors in your onSubmit handler
  • Consider adding loading states during save operations