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​