Skip to content

Guides and examples: email providers, i18n, monorepos, MJML and Beefree, upgrading to 0.3 - #71

Open
ivoIturrieta wants to merge 64 commits into
feat/migrate-mjml-beefreefrom
feat/guides
Open

ivoIturrieta wants to merge 64 commits into
feat/migrate-mjml-beefreefrom
feat/guides

Conversation

@ivoIturrieta

@ivoIturrieta ivoIturrieta commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on the MJML and Beefree importers PR (#70), which is stacked on the 0.3.0 library PR (#69): it shows only the guides and examples. Merge it after both, with its base changed to main first. The repo's tests only run on pull requests into main or develop, so CI starts here once the base is main; until then, the validation below was run locally, with #70's head 72c037d (which has #69's head 11ec4e2) merged in.

What

  • Email providers: the content-workflows example sends with 14 providers. Mailgun, Azure, MailerSend, Scaleway, Plunk, Lettermint, Brevo, Inbound and EUSend are added, each tested against mocked endpoints. Guide: email providers.
  • i18n example: one template in English and Arabic with react-intl, react-i18next and next-intl, including right-to-left output. It runs in CI. Guide: translations and right-to-left, written for 0.3 (inline <FormattedMessage>/<Trans> children, right alignment by default in RTL, Tailwind's logical classes following the direction, a class provider such as <IntlProvider> around the root, and an async component failing a synchronous render).
  • Migrating from MJML: leads with npx @unlayer/migrate <dir> --from mjml: what becomes editable blocks, what stays MJML's HTML, includes, MJML 3, what the check compares against (text made too faint to read fails it), and direction: <mjml dir="rtl"> becomes a right-to-left email only where no section or group shows its columns left to right, since Elements lays out every Row in the email's direction and MJML doesn't. The hand mapping stays, for tuning the output.
  • Migrating from Beefree (new): the JSON it reads, the HTML export to put beside it so the template is checked (text made too faint to read fails it), ? for unverified, and the declared differences.
  • Upgrading: what changes from 0.2 to 0.3 and what to check (17 items: a part that fails fails the render by default, the text block's markup, images sized in their slot, the editor's base stylesheet, the Provider, right-to-left alignment, async components and a <Suspense> around the template, how the plain text differs, React's ids, migrate's range, an app's renderToString providers reaching a template only till Elements learns the context's default, …), what else changed (a class component around the root is unwrapped), the new entries (Tailwind's logical classes follow the text direction; Markdown keeps raw HTML to an allowlist), and the 0.1 to 0.2 guide.
  • Monorepos: one Elements copy, React resolution and workspaces (what two copies of React do with 0.3's renderer), previewing with a small render script.
  • No unpublished packages: the guides don't mention the local preview package or the CLI commands that need it.
  • READMEs: point to the Elements skill in unlayer/unlayer-skills, and list @unlayer/migrate as published, with its MJML and Beefree sources. The changelog only gets a line pointing to the upgrade guide (its 0.1.1 to 0.2.1 history is in @unlayer/react-elements 0.3.0: Tailwind, Markdown, code, fonts, async, codegen and editor-parity fixes #69).

Validation

ivoIturrieta and others added 5 commits October 16, 2026 08:02
… i18n example

The content-workflows example adds Mailgun, Azure, MailerSend, Scaleway,
Plunk, Lettermint, Brevo, Inbound and EUSend, each tested against a
mocked endpoint. The new i18n example renders one template in English
and Arabic with react-intl, react-i18next and next-intl, right to left
included, and runs in CI.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
The monorepo guide previews templates with a small render script, so it
needs nothing that isn't published. The READMEs point to the Elements
skill in unlayer/unlayer-skills, and list @unlayer/migrate as published,
with its MJML and Beefree sources.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
The MJML guide leads with `--from mjml` (what becomes blocks, what stays
MJML's HTML, includes, MJML 3, what the check compares against), with
the hand mapping kept for tuning. The Beefree guide covers the JSON it
reads, the HTML export to check against, and declared differences. Both
were checked by running the CLI on the committed fixtures.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
What changes from 0.2 to 0.3 and what to check (the text block's markup,
images sized in their slot, the editor's base stylesheet, the Provider,
right-to-left alignment, migrate's range), and the 0.1 to 0.2 guide.
The changelog points to it.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
The example runs on Node 20 (its script and test pass there), but the
Azure SDK's packages declare Node 22 or newer, so the guide and the
example's README say to use 22+ to send with Azure.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
@ivoIturrieta
ivoIturrieta marked this pull request as ready for review October 16, 2026 15:24
ivoIturrieta and others added 6 commits October 17, 2026 12:13
The 0.2 to 0.3 section now says that Elements renders templates with a
renderer of its own (no react-dom in a render, each component run once,
Next.js's server layers and edge runtimes, and the Pages Router edge
route's `unstable_allowDynamic` with webpack), that it checks the
installed React (`UNLAYER_SKIP_REACT_CHECK=1`), that a part that fails
throws a `RenderError` by default (`onError` and `omitted` to leave it
out), that async and suspending components fail the synchronous
renderers, and that `useId()` gives React's own ids, with `idPrefix`
among what's new. A Row that doesn't fit its layout fails the render
now, where the guide said it was left out.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
The upgrade guide gives React 18.0 to 18.3 as supported, says that a render
throws by default and that error boundaries don't catch, what a <Suspense>
around the template does, how the plain text differs from 0.2's, that a class
component around the root is unwrapped, and, under what's new, that Tailwind's
logical classes follow the text direction and that Markdown keeps raw HTML to
an allowlist. The i18n guide says an async component fails the render and that
a class provider's root settings are read; the monorepo guide says what two
copies of React do now that Elements renders without react-dom.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
…nt text fails

The MJML guide gave <mjml dir="rtl"> as textDirection="rtl" and a
section's direction="rtl" as always listed. A right-to-left email in
Elements lays out every Row's columns right to left, which MJML's dir
doesn't, so dir="rtl" is kept only where no section or group shows
two or more columns left to right; otherwise the run lists it as not
kept. A section's or group's direction="rtl" is listed only in a
left-to-right email, and the table to approximate it says how columns
render in each direction. The MJML and Beefree guides also say that
clear text turned too faint to read fails the check.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
ivoIturrieta and others added 17 commits October 22, 2026 16:18
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
…plate's error names it

The upgrade guide said Elements is tested with React 19.0: the tests
run on React 18.0, 18.2, 18.3, 19.1, 19.2 and 19.3, as the README now
says. The synchronous renderers now throw a RenderError naming an async
or suspending template (or the component around its root that waits),
as they do for one inside it: the upgrade guide gives the message with
the component's name, and the i18n guide says the error for a template
whose messages are loading is that RenderError.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
0.2 passed an app's renderToString providers through to a context a
template doesn't provide; 0.3 reads the app's value there only before
it has learned that context's default. The 0.2-to-0.3 checklist says
so, and what to do instead: put the provider inside the element
rendered, or pass the value as a prop.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
0.3 writes a javascript: URL in the links and images it writes as one
that throws with React 18 too, where 0.2 wrote it as given there, and a
Button's or Image's link of null renders as none, where 0.2 failed:
both are listed with what else changed.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
…s are skipped

0.3 now writes the editor's exporters' own links (a Button's, an
Image's, an email's Menu and Social) with a javascript: URL as one
that throws too, which 0.2 wrote as given, and skips a null Menu item
or Social icon, where 0.2 failed: the upgrade guide's entries say so.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
ivoIturrieta and others added 30 commits October 24, 2026 07:58
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
… can't add markup

The upgrade guide's "Also changed" said any prop given as `null` is one not
given: only a style prop is; a content prop given as `null` still renders
no content, as in 0.2. A Video's id joins the values that can't add
markup, percent-encoded where it holds what an id doesn't.

Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>
Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <[email protected]>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants