PageKit — the self-hosted GrapesJS site builder, sold as source. Get early access

Email Template Builder

Build an Email Template Builder with GrapesJS

Give your users a visual way to create responsive email templates — reusable blocks, MJML, custom components and production-ready HTML export, all inside your own product.

Open sourceSelf-hostedExtensibleDeveloper-friendly

The loop your users live in

  1. Build
  2. Design
  3. Preview
  4. Export
  5. Send

26k+

GitHub stars

100+

Plugins on GJS.Market

10 years

Active development

BSD-3-Clause

Core license

Try it

A template library, live

Open a template off the shelf, change it, then save it as a new one. The shelf grows by one. That loop — not the individual designs — is what an email template builder gives your users.

your-app.com/templates

Template library

Add a block

  • Your logo

  • Welcome aboard, {{ user.first_name }}

  • Here is how to get your first project running.

  • Open your dashboard

  • Unsubscribe · Manage preferences

Merge tags stay untouched — your renderer resolves them per recipient at send time.

The real editor

Or open the real thing

The demo above is a model of the loop. This is GrapesJS itself, running the official MJML preset — loaded only when you ask for it, so it costs this page nothing until then.

Open in a new tab

The MJML editor demo published by the GrapesJS project itself. Add a section, edit the copy, then look at the generated markup.

grapesjs.com/demo-mjml.htmlFree

Loads in an iframe from the demo's own site.

On GJS.Market

MJML editor is available as a product

Definition

What is an email template builder?

An email template builder lets people create and change email layouts visually instead of hand-writing HTML email markup. A template *library* ships finished designs; a template *builder* ships the ability to make them — and to keep making them after you have shipped.

A template as a file

  1. Write the HTML

    tables, inline styles, conditional comments

  2. Test across clients

    you cannot see the result while writing

  3. Hand it to marketing

    as a file, or pasted into a tool

  4. Every change comes back to you

    a developer ticket per tweak

One template, one owner, one bottleneck.

A template as a system

  1. Open a starting point

    from the shelf, or blank

  2. Edit visually

    the result is on screen the whole time

  3. Save it as a template

    a row your customer owns

  4. Reuse and version it

    no ticket, no developer

Many templates, owned by the people who send them.

Templates give your users a starting point. A builder gives them control.

The distinction that matters

More than an email template library

A library is a shelf with a ceiling: it covers what it was built to cover, and requests above that line have nowhere to go. A builder is a loop — and the shelf grows every time someone goes round it.

Template library

Anything above this line needs a developer

Template builder

  1. Create
  2. Save as template
  3. Reuse
  • "We need a clean welcome email by Friday."

    Covered

    Template library

    Solved. This is exactly what a good library is for — pick one, change the words, send.

    Covered

    Template builder

    Also solved, from the same starting points. A builder ships with a library inside it.

  • "Every tenant needs their own legal footer."

    Not covered

    Template library

    One shared footer, or a fork of every template per tenant.

    Covered

    Template builder

    One footer block, saved once per tenant and reused across their templates.

  • "Put the customer's actual order in the receipt."

    Not covered

    Template library

    Static placeholder text. The data has to be spliced in downstream.

    Covered

    Template builder

    A component bound to your order model, editable as a block and resolved at send time.

  • "Restore the version we sent last Tuesday."

    Not covered

    Template library

    Whatever the file was overwritten with is what you have.

    Covered

    Template builder

    A version row. Restoring is a query, not an archaeology project.

  • "Let our customers build their own, without emailing us."

    Not covered

    Template library

    Not what a library does — it has no authoring surface.

    Covered

    Template builder

    The whole point. Your users create and keep templates you never see.

Ship the library. Then ship the thing that makes more of them.

Capabilities

Everything you need to build emails visually

The editing surface your users get, and the parts of it you control.

Drag & drop

Users build layouts by moving blocks onto the canvas, in the order they want them.

Visual editing

Text, images and links are edited in place, with the rendered result on screen the whole time.

Reusable blocks

Sections your users save once and drop into any template afterwards.

Responsive layouts

Preview and adjust the layout at the widths that matter for email, while editing.

MJML

Author against mjml where you want responsive markup generated for you rather than hand-maintained.

HTML export

Produce the email HTML your delivery workflow already expects.

Assets

Manage the images a template references, in whatever store you already use.

Custom components

Add editable components that mirror your own product's data and workflows.

Undo & redo

A full history stack, so experimenting with someone else's template is safe.

Blocks

Create reusable email blocks

Give users a library of sections to assemble from, instead of making them start every email from an empty canvas. Each one is a block definition you register once.

  • Header

    Logo, preheader text and the navigation row, if you use one.

  • Hero

    The opening statement and the first call to action.

  • Product grid

    A row or grid of items, bound to your catalogue where you want it live.

  • Feature list

    Short icon-and-text rows for release notes and onboarding.

  • Pricing

    Plan comparison laid out to survive a table-based renderer.

  • Testimonial

    A quote, an attribution and an optional avatar.

  • Call to action

    A bulletproof button that renders without background images.

  • Footer

    Address, preferences link and the unsubscribe link you are legally required to include.

Custom components

Build email components for your product

Standard blocks cover layout. What makes the builder yours is the components that know about your data — an order, a subscription, a booking, a recommendation.

  1. Standard
  2. Composed
  3. Bound
  4. Your data model

Standard

Text, image, button, columns, section

The primitives every email needs. They come with the editor.

Composed

Header, hero, product row, footer

Your standard blocks, assembled once and registered for reuse.

Bound

Order, customer, event, recommendation

Components with traits your users set and merge tags your renderer resolves per recipient.

The editor renders a placeholder while the template is being written. Your backend swaps in the real values at send time.

Registering a component bound to your modeljs
// A component bound to your own model. The editor renders a placeholder; your
// backend swaps in the real order at send time, per recipient.
editor.DomComponents.addType('order-summary', {
  model: {
    defaults: {
      droppable: false,
      // Traits are the fields the person editing the template can set.
      traits: [
        { name: 'orderId', label: 'Order', type: 'text' },
        { name: 'showTax', label: 'Show tax line', type: 'checkbox' },
      ],
      // Merge tags survive export untouched and are resolved by your renderer.
      components: '<mj-text>{{ order.total }}</mj-text>',
    },
  },
});
Template types

Start from a template

In a template system, templates differ by who owns them and who may change them — not only by what they say. These are the classes worth modelling from day one.

Ownership classes

  • System template
  • Tenant template
  • User draft
  • Locked brand template
  • Shared snippet
  • Blank

These are ownership classes in a template system, not products for sale. GJS.Market lists editor plugins and presets; it does not sell email templates.

Responsive

Build responsive email templates visually

Preview the layout at the widths email actually renders at, while you are still editing it. 600px is the long-standing convention for the body column — a convention, not a specification.

Desktop600px
Tablet480px
Mobile320px

What users can change per width

  • Stack columns
  • Font size
  • Padding
  • Image width
  • Button width
  • Hide a block

Two columns on a desktop client, stacked on a phone — decided in the template, not by the recipient's client guessing.

Compatibility

Design for real email clients

Email clients implement HTML and CSS differently, and always have. A visual builder does not remove that — it standardises the authoring step so the same known-good markup comes out every time.

ClientWhat it does differentlyWhat the builder does about it
GmailStrips <style> blocks in some contexts, including the Gmail mobile apps with non-Gmail accounts.Emit inlined styles rather than relying on a stylesheet surviving the trip.
Outlook (Windows)Renders with the Word engine: no flexbox, no grid, and border-radius on a table cell is dropped.Generate table-based layout and bulletproof buttons instead of CSS layout.
Apple MailThe most standards-complete of the four; also the most likely to expose a layout that only worked by accident elsewhere.Preview at each width while editing, so the layout is deliberate rather than incidental.
Yahoo MailHistorically inconsistent with media queries; current behaviour is Not publicly documented.Prefer layouts that degrade to a single readable column without a media query.

Constraints as verified on 2026-08-28. Behaviour changes; check caniemail.com for the current state, and test production emails against the clients your own audience actually uses.

MJML

Build responsive emails with MJML

mjml exists to make responsive email markup something you generate rather than hand-maintain. GrapesJS can be extended with grapesjs-mjml to put a visual editing surface on top of it.

A source you can diff

MJML is compact enough to review in a pull request, which raw email HTML is not.

Responsive by construction

The compiler emits the table scaffolding and media queries, so nobody hand-writes them.

Stored, not just exported

Keep the MJML alongside the project JSON, so a template stays editable as source.

Architecture

Templates as data

The question that decides whether you have a builder or a viewer: what rows exist after a user clicks Save? Three tables cover it.

templates

The template

  • idprimary key
  • tenant_idwho owns it
  • namewhat users see
  • kindsystem, tenant or draft
  • current_version_idpoints at the live version

One row per template, scoped to a tenant.

has many

template_versions

Every save

  • idprimary key
  • template_idparent template
  • versionincrements on save
  • project / mjml / htmlthe three artifacts
  • created_bywho saved it, and when

Append-only. Restoring last Tuesday's email is a query.

composed from

blocks

Reusable sections

  • idprimary key
  • tenant_idwho may use it
  • nameappears in the block rail
  • projectthe saved section
  • lockededitable, or brand-locked

Saved once, dropped into any template afterwards.

One version row holds all three

JSON

Project data

What you load back into the editor. Without it a template is read-only forever.

MJML

Editable source

Diffable, reviewable, and re-compilable when the compiler improves.

HTML

What you send

Rendered and inlined, handed to your provider unchanged.

Store all three. Each answers a different question, and reconstructing any one of them from the others is lossy.

How the three formats relate
Your infrastructure

Keep your email data in your own infrastructure

The editor is a client. It asks your API for a template and hands one back; it never learns who your tenants are, and it never holds the only copy of anything.

Where a template goes

  1. Editor
  2. Project data
  3. Your API
  4. Your database
  5. Your email infrastructure
Storage scoped to a tenant and a templateJS
// Storage scoped to the tenant AND the template. The editor never learns who
// the tenant is — your API does, from the session, and refuses everything else.
editor.Storage.add('remote', {
  async load({ templateId }) {
    const res = await fetch(`/api/templates/${templateId}/project`);
    return res.json();
  },
  async store(project, { templateId }) {
    await fetch(`/api/templates/${templateId}/project`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(project),
    });
  },
});

Integrate the editor with the backend you already run, rather than moving your email workflow onto someone else's platform. GJS.Market sells editor plugins — it does not host your templates and does not send your email.

Multi-tenant

Your customers' libraries, not one shared shelf

The moment a second customer saves a template, ownership stops being a detail. Three tiers cover almost every product.

System templates

Yours. Visible to every tenant, editable by none of them — the starting points you maintain.

Tenant templates

Saved by a customer, visible to their whole team, invisible to everyone else.

User drafts

Work in progress. Private until someone promotes it to a tenant template.

Use cases

Add an email builder to your SaaS

The same template system, pointed at different jobs.

Build vs buy

Build an email editor from scratch?

The editing canvas is the visible part. A template system is the rest of it — and the rest is where the time goes.

What a template system is made of

  • Editor canvas
  • Drag and drop
  • Block registry
  • Component model
  • Style controls
  • Responsive preview
  • Asset management
  • Template CRUD
  • Versioning and restore
  • Merge-tag validation
  • Tenant scoping
  • Undo and redo
  • HTML export
  • MJML compilation
  • Ongoing client testing

From scratch

All of it

  • Every subsystem above is yours to design, build and keep working
  • Email client behaviour changes underneath you, indefinitely
  • None of it is the product your customers are paying for

With GrapesJS

The editing layer, already built

  • Canvas, drag and drop, blocks, components, styling, assets and history
  • Storage APIs to wire the template rows to your own backend
  • A plugin ecosystem for MJML, blocks, storage and export

GrapesJS gives you the editing layer. The template system on top of it — ownership, versioning, tenancy — is yours, and should be: it is the part that matches your product.

Plugin ecosystem

Extend your email builder with GJS.Market plugins

Real listings from the catalogue, grouped by the job they do inside a template system. Names and prices come straight from the marketplace, so nothing here can drift from what is actually for sale.

Recommended stacks

Build the email builder you need

Four combinations that already work together, chosen by whose template library you are building.

Prices are read live from the catalogue at build time.

Delivery

From a stored template to an inbox

GrapesJS is the editing layer and stops at the HTML. Everything after that is your backend and the provider you already pay — there is no sending built into the editor, and none implied here.

  1. Stage 01

    Pick a version

    The send references a specific version, so what went out stays reproducible after the template moves on.

    template_versions.id
  2. Stage 02

    Merge the data

    Your renderer resolves the merge tags per recipient. The editor never sees customer data.

    {{ order.total }}
  3. Stage 03

    Render the HTML

    Table-based and inlined, which is what every provider expects to be handed.

    text/html
  4. Stage 04

    Call your provider

    Whichever ESP you already use. Swapping it changes nothing about the template system.

    POST /messages
  5. Stage 05

    Feed results back

    Opens, clicks and bounces attach to the version that produced them, which is what makes them comparable.

    webhook
Nothing in this chain runs on GJS.Market. The editor produces markup; your infrastructure does the rest.
Quick start

Start building your email template builder

The editor core plus the official MJML plugin. Both are BSD-3-Clause.

npm install grapesjs grapesjs-mjml
Mounting the editor on a templateJS
import grapesjs from 'grapesjs';
import grapesjsMjml from 'grapesjs-mjml';
import 'grapesjs/dist/css/grapes.min.css';

// One editor instance per template being edited. The template id — not a page
// id — is what every load and store call is keyed by.
const editor = grapesjs.init({
  container: '#template-editor',
  plugins: [grapesjsMjml],
  pluginsOpts: {
    [grapesjsMjml]: {
      imagePlaceholderSrc: 'https://cdn.your-app.com/email/placeholder.png',
    },
  },
  storageManager: {
    type: 'remote',
    autosave: true,
    stepsBeforeSave: 10,
  },
});

Saving is where a template builder stops being an editor. "Save as template" creates a row your customer owns; saving again appends a version rather than overwriting one.

Creating a template and a versionJS
// "Save as new template" — a row your customer owns from now on.
async function saveAsTemplate(name) {
  const res = await fetch('/api/templates', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      name,
      project: editor.getProjectData(), // re-openable in the editor
    }),
  });
  return res.json(); // { id, name, version: 1 }
}

// Editing an existing template appends a version rather than overwriting it,
// so "restore the version we sent last Tuesday" stays a query, not a restore.
async function saveVersion(templateId) {
  await fetch(`/api/templates/${templateId}/versions`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ project: editor.getProjectData() }),
  });
}
  1. Install the core and the MJML plugin
  2. Mount the editor, keyed by template id
  3. Register your blocks and bound components
  4. Wire storage to your own API
  5. Add save-as-template and versioning
  6. Hand the rendered HTML to your provider

Framework specifics live on their own pages — this page stays framework-neutral on purpose.

Audience

Who builds email template builders?

Teams that need email creation to happen inside their product rather than beside it.

SaaS companies

Embed visual email editing in your application, so customers stop exporting to a separate tool.

Email platforms

Give customers a campaign builder whose templates you can version, audit and support.

Developers

Start from an extensible editing foundation instead of writing a canvas and a history stack.

Agencies

Build a reusable email system per client, handed over as blocks and locked templates.

Product teams

Add email creation without owning an editor core and its maintenance forever.

Marketing platforms

Let users produce on-brand email without writing markup or filing a ticket.

Related solutions

Looking for something more specific?

This page is about the template system. These cover the neighbouring questions.

FAQ

Email template builder questions

Get started

Build emails visually. Build the editor your way.

Start with GrapesJS, shape the editing experience around your product, and extend it with the plugins your template system needs.

Do it yourself

Start building

Install the core, mount the editor and wire storage to your own API.

Read the quick start
Extend it

Browse email plugins

Presets, blocks, storage and export — real listings with live prices.

Browse email plugins
Hand it over

Build a custom email builder

Tell us what your template system has to do, and we will scope the build.

Start a brief

Related reading