Skip to main content

Limitations

Elements covers most email/page/document use cases out of the box, but it isn't a fully general HTML renderer. Some constraints come from how the library is structured, others come from the reality of how email clients render HTML. This page collects both.


Structural Constraints

The tree must follow this shape exactly:

<Email | Page | Document | Body>
<Row>
<Column>
...content components...
</Column>
</Row>
</...>

Concretely:

  • Content components must live inside a <Column>. They cannot sit directly inside a <Row> or a root wrapper.
  • <Column> must live inside a <Row>. It cannot sit directly in a wrapper.
  • <Row> must live inside a wrapper (<Email>, <Page>, <Document>, or <Body>).
  • The <Column> count must match the chosen layout (or the length of cells). Mismatch throws via validateColumnLayout.
  • renderToJson requires the root to be <Body>, <Email>, <Page>, or <Document>. Any other root throws.
  • renderRowToJson requires the root to be <Row>. Any other root throws.

Common Mistakes

The footguns we see most often. Each one maps to a Critical Rule in Configuration.

  1. fontFamily as a string. Must be { label: string, value: string }, never a plain string.
  2. fontWeight as a string. Must be a number (400, 700), never "400".
  3. Column count mismatch. TwoEqual wants two <Column> children, ThreeEqual wants three, and so on.
  4. Items rendered directly in <Row>. Wrap them in a <Column> first.
  5. Columns rendered directly in a wrapper. Wrap them in a <Row> first.
  6. text prop on <Paragraph>. Use html (rich) or children (plain) instead.
  7. Bare numeric strings for padding. Use "0px", not "0".
  8. Forgetting the wrapper. A bare <Row> without an <Email> / <Page> / <Document> / <Body> ancestor has no rendering mode set.
  9. <UnlayerProvider> without a wrapper inside. The provider only delivers config to children when there's a wrapper (<Email>, <Page>, <Document>, or <Body>) underneath it. Components placed inside the provider but outside a wrapper don't see the config.
  10. <UnlayerProvider> in a Server Component. The provider is "use client". For SSR, pass config directly to the render function or to <Body>.

Email-Client Constraints

Email clients limit what HTML+CSS will render reliably. The <Email> wrapper papers over the worst offenders, but you'll still run into things like:

  • Embedded video doesn't play. <Video> renders a clickable thumbnail that links to the source, not an inline player. Inline <video> tags are stripped almost everywhere.
  • Custom fonts behave inconsistently. Outlook on Windows in particular falls back to a system font. Stick to the common font stacks for predictable results.
  • Dark-mode rendering varies by client. Apple Mail and Outlook on macOS both apply automatic color inversion that surprises designers. Test both modes if dark mode matters to you.
  • <style> blocks may be stripped. Inline styles always survive, and Elements uses inline styles for everything that can be inlined. The leftover <style> block (hover effects, media queries) belongs in <head> — that's where Gmail, Yahoo, and most webmail clients actually keep it. Use renderToHtmlParts to split head from body instead of dumping the whole document into <body>.

See Also