_emailmd_

Stability & Versioning

What the semver contract covers, and what it deliberately doesn't.

emailmd follows Semantic Versioning. This page defines what the version number makes promises about. The core idea:

The contract is the input language and the API, not the output bytes. Your markdown keeps meaning the same thing; the exact HTML that expresses it may change.

Covered by Semver

Breaking any of these requires a major version bump:

The render() API

  • The render(markdown, options?) signature and its Promise<RenderResult> return shape (html, text, meta, warnings).
  • Every documented RenderOptions field and its accepted values.
  • Existing RenderWarning stages and the guarantee that warnings never turn into throws.

The input language

  • Documented markdown behavior: headings, lists, tables, task lists, emoji shortcodes, inline HTML passthrough.
  • Directive names and their documented parameters (::: callout, bg=, center, …).
  • Button syntax: {button}, variants, color/width/fallback/border-radius attributes.
  • Frontmatter keys: preheader, theme, the theme override keys, fonts, and the passthrough of custom keys into meta.
  • Template tag passthrough ({{ x }}, {% x %}, ${x}, %%x%%, [[x]]).

The rest of the public surface

  • Exported functions and types: buildHead, segmentsToMjml, the theme objects and helpers, the sanitize helpers, WrapperFn/WrapperMeta, Segment.
  • CLI arguments and flags, exit codes, and stdin/stdout behavior.
  • Node.js version support (currently >=20); raising the floor is a major.

Rendering semantics

  • What the output means: a callout renders as a visually distinct card, a primary button uses buttonColor, the preheader appears in the inbox preview, valid input never gains new warnings. Changes that alter how a documented feature looks or behaves in mainstream email clients are treated as breaking even if the API is untouched.

Not Covered by Semver

These may change in minor or patch releases:

  • Exact HTML output. The generated markup structure, attribute order, whitespace, class names, and MJML-generated boilerplate. Don't byte-snapshot the output in your tests. Assert on content and semantics instead (e.g. "contains the button text and href"), the way emailmd's own test suite does.
  • The bundled MJML version. MJML minor/patch upgrades ship in emailmd minors or patches when output semantics are preserved.
  • Warning message text. The stage and the presence of a warning for a given problem are stable; the human-readable message wording is not.
  • Internal formats. The <!--EMAILMD:...--> marker namespace (reserved; user comments in it are dropped), template shielding placeholders, and the intermediate MJML document handed to mjml2html.
  • Undocumented behavior. If it's not in these docs, it's an implementation detail, even if it happens to work today.

Additive Changes Land in Minors

New capabilities that don't change existing behavior arrive in minor versions: new RenderOptions fields, new frontmatter keys, new directives or directive params, new warning stages, new exports, new theme keys (with defaults matching prior rendering), and new CLI flags. Code written against an older minor keeps working.

If you exhaustively match on union types like RenderWarning['stage'] or SegmentType, include a default case; those unions grow in minors.

Deprecation Policy

Features are deprecated in a minor release (documented here and in the changelog, with a migration path) and removed no earlier than the next major.

Before 1.0

Until 1.0, minor versions (0.x → 0.y) may include breaking changes; they are always called out in the changelog with migration notes. Patch releases are always safe. From 1.0 on, the full contract above applies.

On this page