> ## Documentation Index
> Fetch the complete documentation index at: https://docs.allgoodhq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Write an email component

> Structure a new Email Component's MJML, mark its EDIT and LOCKED boundary, and know when a Liquid citation belongs in one.

Writing a component starts with a decision. What may a future editor change, and what must stay fixed so the block works everywhere it is used? This article covers that decision, the MJML itself, and the one place Liquid belongs in a component.

**Prerequisites**

* Familiarity with the MJML tags `mj-section`, `mj-column`, `mj-text`, `mj-button`, and `mj-image`. If they are new to you, read [How MJML works](https://docs.allgoodhq.com/mk/developer/email/how-mjml-works.md). For allGood's EDIT and LOCKED convention, read [Email components](https://docs.allgoodhq.com/mk/content/email/components/index.md).
* Access to a component library's **Workshop** in allGood. This is where you write and save a component.

## What makes a component worth adding

A component earns a place in the library when it does one nameable job, such as a hero, a footer, or a two-column offer row. Describe it in plain language so that someone choosing between it and a similar block on **Overview** picks correctly the first time. A component that covers three loosely related layouts is usually two or three components that have not been split yet.

## Steps

**1. Decide what is EDIT and what is LOCKED before you write markup**

EDIT is anything a send genuinely varies: copy, links, colors, and padding. LOCKED is the element tree. That means which sections and columns exist, what is nested inside them, and their order. Decide this first. The markup and its documenting comment both follow from it.

**2. Build the structure from MJML tags, not raw HTML**

Use `mj-section` and `mj-column` for layout, `mj-text` for copy, `mj-button` for any call to action, and `mj-image` for images. Put real placeholder copy directly in the tags. Write a headline that reads like a headline and a button labeled like a button, not a note about what should go there:

```xml theme={null}
<mj-section background-color="#1a1a1a">
  <mj-column>
    <mj-text font-size="18px" color="#ffffff">
      "Best decision we made this quarter."
    </mj-text>
    <mj-text font-size="14px" color="#999999">
      — Placeholder Name, Placeholder Title
    </mj-text>
  </mj-column>
</mj-section>
```

**3. Document the EDIT and LOCKED boundary in an ag: comment**

Put the comment at the top of the file. Say what the component is for, then list what is editable and what is fixed:

```xml theme={null}
<!-- ag: pull quote on a dark panel. One quote, one attribution line.

     EDIT: the quote text, the attribution line, background-color and
     the two text colors.
     LOCKED: the element tree. One section, one column, two text
     blocks in this order. Adding, removing or re-nesting an element
     is drift, and the validator flags it on every email built from
     this block. -->
```

A future editor, human or allGood's AI, reads this comment before touching the component. A boundary that is not documented here does not exist for anyone else.

The `EDIT:` and `LOCKED:` labels are not the only acceptable style. Plain-language guidance works if it is unambiguous. The library's own `hero` block documents itself this way:

![The hero block's MJML source, with its inline ag: comment describing what is safe to change.](https://mintcdn.com/allgoodtechnologyinc/vWyFubp1-YTCUXDt/images/mk/content/email/components/hero-mjml-source.png?fit=max\&auto=format\&n=vWyFubp1-YTCUXDt\&q=85\&s=12a4709412c9965192e75c9f1cb0c4ea)

The hero block's MJML source, with its inline ag: comment describing what is safe to change.

**4. Use Liquid only for values that must resolve at send time**

An unsubscribe link, a postal address, and a copyright year come from the send itself, not from a person filling in a field. Cite them instead of typing placeholder copy:

```xml theme={null}
<mj-text font-size="12px">
  {{ sender.postalAddress }} — {{ "now" | date: "%Y" }}
</mj-text>
<mj-text font-size="12px">
  <a href="{{ send.unsubscribeUrl }}">Unsubscribe</a>
</mj-text>
```

Keep everything else as plain text, including headlines, button labels, and body copy. A component is not where per-send personalization happens. That belongs to a template's `copySchema`, which sits on top once components are composed.

**5. Preview and iterate in the Workshop**

Open the component in the **Workshop**. Describe changes to **Ask Mary** in plain language. You can view a component's MJML, but you can't edit it by hand. Each save creates a new version. A version does not affect a live email until it is published, so iterating costs nothing.

**6. Track the component through publishing**

In the **History** tab, confirm your new version shows a **Published** entry. For History, rollback, and checking what a component is used by, see [Setting up and reviewing email components](https://docs.allgoodhq.com/mk/content/email/components/setting-up-and-reviewing-email-components.md).

The component is ready when it appears on **Overview** with an accurate description and preview, and its status shows the version you published.

## If something is not working

**The validator flags your component every time it is used.** Something inside the LOCKED boundary changed. An element was added, removed, or re-nested. Revert the structural change, and limit your edit to what the `ag:` comment lists as EDIT.

**A Liquid token renders literally instead of resolving.** allGood does not resolve that token at the component level. Use confirmed system values only, such as `send.unsubscribeUrl`, `sender.postalAddress`, or a `"now" | date` filter. Anything else belongs in plain text, or in a template's `copySchema` if it needs to vary per send.

If neither fix explains what you see, contact the team that maintains allGood's email tooling. Include the component's name and the version you are comparing against.

## Related articles

* [Email components](https://docs.allgoodhq.com/mk/content/email/components/index.md)
* [How MJML works](https://docs.allgoodhq.com/mk/developer/email/how-mjml-works.md)
* [Setting up and reviewing email components](https://docs.allgoodhq.com/mk/content/email/components/setting-up-and-reviewing-email-components.md)
* [Compare MJML with other email tools](https://docs.allgoodhq.com/mk/developer/email/compare-mjml-with-other-tools.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.