zylior
← Blog

Why a typed-blocks blog beats WYSIWYG

You write an article, you click bold, you paste an image, you publish. Three months later, you want that same content in a newsletter and an RSS feed, and that's where it falls apart: `<span style>` tags slapped in from Word, a table that breaks on mobile, a duplicated h1 that wrecks your SEO. The problem isn't your editor — it's that you store free HTML instead of storing data.

At Zylior we treat every article as a list of validated typed blocks, not an HTML soup. That changes everything: a single piece of content renders to a web page, an email and plain text, without ever breaking. Here's why this model beats WYSIWYG, with the technical detail.

WYSIWYG stores HTML, and HTML drifts

A WYSIWYG editor produces an HTML string that you stash in your database. The catch: that string is unconstrained. Nothing stops an orphan `<div>`, a `style="font-family:Calibri"` pasted from Word, an `<h1>` in the middle of the body, or a tag that's never closed. You don't see it in the editor — it "fixes" it silently on display — but the day you pull that HTML out of its context (email, mobile app, API), it breaks.

An article = validated data, not markup

The alternative: you never store HTML, but an array of typed blocks, each with a known shape validated at write time. A paragraph is `{type:"p", text}`; a section heading is `{type:"h2", text}`. Inline is restricted to a whitelist (`bold`, `italic`, https links). Anything that doesn't fit the schema is rejected on save, not "tolerated on display".

{
  "meta_title": "Blocs typés > WYSIWYG",
  "blocks": [
    { "type": "p", "text": "Accroche en **gras** et [un lien](https://zylior.com)." },
    { "type": "h2", "text": "Une section" },
    { "type": "list", "ordered": false, "items": ["Item 1", "Item 2"] },
    { "type": "callout", "tone": "tip", "text": "Un encadré." }
  ]
}

From there, you validate with a schema (Zod, JSON Schema, whatever): `type` in the enum, `text` capped at N characters, `items` an array of strings, `tone` in `{info,warn,tip}`. An invalid article cannot be saved. You move the rigor from display to write time — where an error costs an error message, not a page broken in prod.

One content, three guaranteed renders

This is the real operational win for a solo founder. Because a block is semantic data ("this is a heading", "this is a list") and not markup, you write one renderer per target and each block knows how to render anywhere:

  1. Web: `h2` → `<h2 class="...">` with your design system, `code` → syntax highlighting.
  2. Email: the same `h2` → an inline-styled HTML table compatible with Gmail/Outlook; the forbidden types can't even show up.
  3. Plain text / RSS / LLM: `h2` → `## Title`, `list` → markdown bullets. Perfect for summarizing an article for an agent or feeding a digest.
  4. JSON-LD: you automatically derive `articleBody`, `headline`, `wordCount` from the blocks, without parsing any HTML.
A rule we apply: if a render (email, RSS, plain text) can't express a block type, the block is forbidden at the source. That's why we ban `img` and `cta` in the body — they don't survive email cleanly. Better a poor, reliable format than a rich, broken one.

Preview = published, and SEO genuinely under control

With free HTML, your preview often uses a different rendering engine than prod (the editor vs the site): what you see isn't what ships. With typed blocks, the preview calls exactly the same web renderer as publication. Zero surprise: if the preview looks right, the published version is identical. On the SEO side, because the structure is guaranteed, you control what WYSIWYG lets drift: a single `h1` (derived from `meta_title`, never in the body), a consistent `h2`/`h3` hierarchy, a `meta description` capped at 140-160 characters, a clean `canonical`, and an `Article`-type `JSON-LD` derived mechanically from the blocks. Your markup is valid by construction, not by hope.

Classic WYSIWYG trap: the author makes the title big inside the body, which creates a second `h1`. Google sees two competing titles, you lose ranking clarity. With blocks, the h1 lives outside the body and the author literally cannot reinject one.

To be honest, it's not free: you maintain your renderers and the editor is more constrained than an "anything goes" WYSIWYG (no magic Word paste). For a SaaS portfolio blog where you want reliability and multi-channel, it's the right trade-off. Concretely, to get started: define an enum of types (`p`, `h2`, `h3`, `list`, `code`, `callout`, `quote`), write a schema that rejects everything else, wire up a web renderer and a text renderer (email comes later), and point your preview at the prod web renderer. You'll have a blog where the preview never lies, where the same article ships in a newsletter without rework, and where your SEO is correct by default — not through manual vigilance.

The newsletter

By subscribing you agree to receive the Zylior newsletter. One-click unsubscribe in every email.