Skip to content
Mail Quilt
Esc
↑↓navigate↵open⌘Jpreview
On this page

Shell

Put a ready-made email editor page in your app, with a header, Export and Import buttons, messages and saving in the browser.

<mail-quilt> is only the editor. Around it, your app still needs somewhere to export the email, somewhere to show why an email was refused, and a way to keep the person’s work. <mail-quilt-shell> is all of that in one element: the editor, with a header above it and a message bar between them.

What it has

The header has the heading and up to three buttons. Export HTML downloads the email as email.html, ready to send. Export JSON downloads it as email.json, the document format the editor saves. Import JSON opens a .json file. If the editor refuses it, the message bar says why and the current email stays.

The message bar shows emails the editor refuses, exports that fail and saves that fail. Screen readers announce each message. The Playground is a Shell.

With a bundler

npm install @mailquilt/editor
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Email editor</title>
    <style>
      body {
        margin: 0;
      }

      /* The Shell fills the space you give it. */
      mail-quilt-shell {
        height: 100dvh;
      }
    </style>
  </head>
  <body>
    <mail-quilt-shell save-key="my-app:email"></mail-quilt-shell>
    <script type="module" src="./main.js"></script>
  </body>
</html>
import "@mailquilt/editor";
import { starterEmail } from "./starter-email.js";

// Opens when nothing is saved under save-key yet.
document.querySelector("mail-quilt-shell").value = starterEmail;

Importing @mailquilt/editor registers both <mail-quilt> and <mail-quilt-shell>. On a page with no build step, load the CDN build as the quick start shows. It registers both too.

The Shell fills the width and height of the space you give it, and the editor takes everything below the header. If the email is longer than that, the editor scrolls inside the Shell.

React

npm install @mailquilt/react
import { MailQuiltShell } from "@mailquilt/react";

import { starterEmail } from "./starter-email";

// A Server Component, as the Shell needs no callbacks to save the email.
const Page = () => (
  <div style={{ height: "100dvh" }}>
    <MailQuiltShell saveKey="my-app:email" value={starterEmail} />
  </div>
);

export default Page;

MailQuiltShell renders an empty <mail-quilt-shell> on the server, with its attributes, and the editor loads in the browser. Because saveKey keeps the email, this page can stay a Server Component. Pass onChange or onError from a Client Component, as with MailQuilt.

Saving

With save-key, the Shell keeps the email in the browser’s localStorage under that key:

  • When the Shell opens, an email saved under the key opens instead of value. value is the email to start from when nothing is saved yet.
  • While the saved email is open, setting value to nothing, or to that same starting email again, leaves it open. A React component that renders again, or gets its starting email after the Shell opens, doesn’t replace the person’s work. Setting a different email opens it as usual.
  • If the saved email can’t be opened, the message bar says why and value opens instead. The saved email stays as it is until the person changes something, so nothing is lost by opening the page.
  • Each change is saved under the key. If the browser can’t save, because storage is blocked or full, the message bar says so, and suggests Export JSON while that button shows.

Pick a key no other app on the same origin uses, such as one starting with your app’s name. The Shell reads the key once, when it opens, so set it before then, as the examples do. Removing it later stops the saving.

Opening the saved email doesn’t fire mq-change, because the person hasn’t changed anything. If your app keeps its own copy of the email, read value from the element once it has loaded, such as from the ref in React, rather than assuming it’s the email you passed.

Without save-key, the Shell stores nothing. Save the email yourself from mq-change, as the quick start does. mq-change fires with or without save-key, so you can keep the email in the browser and on your server too.

The Shell’s API

Name What it does
value The email the Shell shows, as on <mail-quilt>. With save-key, a saved email opens instead of the first one you set.
heading The text in the header. It’s “Email” when unset.
actions The buttons to show, by name and separated by spaces: import-json, export-json and export-html. Leave it out to show all three.
save-key Where the email is kept in the browser, as the saveKey property. Leave it out to store nothing.
mq-change Fired after every change, including an import. The new email is in event.detail.document.
mq-error Fired when the editor refuses an email you set as value. The reason is in event.detail.reason, and the message bar shows it too.
exportHtml() Returns a promise of the email’s HTML, as on <mail-quilt>.

value, mq-change, mq-error and exportHtml() follow the rules the quick start describes, including when to listen for mq-error.

In React, these are the value, heading, actions, saveKey, onChange and onError props, with actions as an array of names, and exportHtml() on the ref.

Customizing it

Set heading to name what’s being edited, such as heading="Welcome email".

Choose the buttons with actions. An app that keeps emails on its own server may want only Export HTML, so people don’t move emails around as files:

<mail-quilt-shell actions="export-html"></mail-quilt-shell>
<MailQuiltShell actions={["export-html"]} />

The buttons keep their own order, whatever order you name them in, and actions="" shows none. Without Export JSON, a failed save says the browser can’t save without suggesting it. If you leave out Export HTML, call exportHtml() from a control of your own, so people can still get the HTML.

The Shell draws its header and messages from the same theme tokens as the editor. Set them on <mail-quilt-shell>, and the editor inside uses them too.

To restyle the Shell beyond the tokens, each piece of the header and the message bar is a CSS part. Style it from your own stylesheet with ::part():

Part What it is
header The bar above the editor.
heading The heading’s text.
actions The group of buttons on the right of the header.
import-json The Import JSON button, while it shows.
export-json The Export JSON button, while it shows.
export-html The Export HTML button, while it shows.
messages The message bar, below the header.
message Each message in the bar.
mail-quilt-shell::part(header) {
  background: #f8fafc;
}

mail-quilt-shell::part(heading) {
  font-size: 16px;
}

Shell or <mail-quilt>?

Use the Shell when the editor is a page of its own and its header can be the page’s controls. It gets an editor page working with a few lines, and a refused email or a failed save always reaches the person.

Use <mail-quilt> on its own when the editor sits inside a page your app already lays out, such as beside a form with the email’s subject and recipients, or when you want buttons of your own in the header. You can choose and restyle the Shell’s buttons, but you can’t add to them.