<?xml version="1.0" encoding="UTF-8"?>
<post>
  <id>01a024a8-0c00-78f9-8627-034cad93673c</id>
  <type>post</type>
  <thread>
    <item>
      <id>01a024a8-0c00-78f9-8627-034cad93673c</id>
      <depth>0</depth>
      <author>SEBASTIEN PORTRAIT</author>
      <url>https://vutuv.de/sebastien_portrait/posts/01a024a8-0c00-78f9-8627-034cad93673c</url>
      <published_on>2026-08-21</published_on>
      <in_reply_to_id/>
      <author_username>sebastien_portrait</author_username>
      <body_markdown>Coelho - Structured rich text for Phoenix

I&apos;ve just published Coelho, a structured rich text library for Phoenix.

The idea is somewhat inspired by Rails&apos; Action Text: provide a proper rich text layer between the editor in the browser and the application&apos;s data model.

But instead of storing HTML, Coelho stores the document as a structured JSON tree — the same kind of document model used by ProseMirror.

That gives the application a single source of truth for both the editor and the server:

* define the document schema once in Elixir
* export the schema to ProseMirror
* validate documents before they reach the database
* store the document directly in a `jsonb` column
* render it to HTML or extract plain text
* customise rendering without changing the stored document
* use different, restricted schemas for different rich text fields
* validate and cast rich text directly through Ecto
* use it from Phoenix LiveView with an included editor
* support attachments with URLs resolved at render time

One of the things I particularly wanted to avoid is treating HTML as the data model.

When rich text is stored as HTML, the markup becomes part of the persisted data. With a structured document instead, the data remains queryable and migratable, while rendering stays an application-level decision.

Coelho also deliberately keeps the core small: no file storage, image processing or collaborative editing. Those are integration points rather than responsibilities of the document layer.

It&apos;s early, but the core is already in place and tested.

I&apos;d particularly love feedback from people building Phoenix applications that need rich text, especially if you&apos;ve used Action Text or another structured rich text solution before.

HexDocs: https://coelho.hexdocs.pm/readme.html

Hex.pm: https://hex.pm/packages/coelho

https://github.com/nseaSeb/coelho</body_markdown>
      <in_reply_to_author/>
    </item>
  </thread>
  <description>Coelho - Structured rich text for Phoenix</description>
  <title>SEBASTIEN PORTRAIT · 2026-08-21</title>
  <author>
    <name>SEBASTIEN PORTRAIT</name>
    <username>sebastien_portrait</username>
    <url>https://vutuv.de/sebastien_portrait</url>
  </author>
  <replies/>
  <url>https://vutuv.de/sebastien_portrait/posts/01a024a8-0c00-78f9-8627-034cad93673c</url>
  <formats>
    <json>https://vutuv.de/sebastien_portrait/posts/01a024a8-0c00-78f9-8627-034cad93673c.json</json>
    <text>https://vutuv.de/sebastien_portrait/posts/01a024a8-0c00-78f9-8627-034cad93673c.txt</text>
    <markdown>https://vutuv.de/sebastien_portrait/posts/01a024a8-0c00-78f9-8627-034cad93673c.md</markdown>
    <xml>https://vutuv.de/sebastien_portrait/posts/01a024a8-0c00-78f9-8627-034cad93673c.xml</xml>
  </formats>
  <tags>
    <item>Elixir</item>
    <item>rich-text</item>
    <item>phoenix</item>
    <item>library</item>
  </tags>
  <in_reply_to/>
  <generated_at>2026-08-31T07:18:22Z</generated_at>
  <schema_version>3</schema_version>
  <like_count>1</like_count>
  <published_on>2026-08-21</published_on>
  <images/>
  <license/>
  <review/>
  <fediverse_reaction_count>0</fediverse_reaction_count>
  <reply_count>0</reply_count>
  <fediverse_replies/>
  <body_markdown>Coelho - Structured rich text for Phoenix

I&apos;ve just published Coelho, a structured rich text library for Phoenix.

The idea is somewhat inspired by Rails&apos; Action Text: provide a proper rich text layer between the editor in the browser and the application&apos;s data model.

But instead of storing HTML, Coelho stores the document as a structured JSON tree — the same kind of document model used by ProseMirror.

That gives the application a single source of truth for both the editor and the server:

* define the document schema once in Elixir
* export the schema to ProseMirror
* validate documents before they reach the database
* store the document directly in a `jsonb` column
* render it to HTML or extract plain text
* customise rendering without changing the stored document
* use different, restricted schemas for different rich text fields
* validate and cast rich text directly through Ecto
* use it from Phoenix LiveView with an included editor
* support attachments with URLs resolved at render time

One of the things I particularly wanted to avoid is treating HTML as the data model.

When rich text is stored as HTML, the markup becomes part of the persisted data. With a structured document instead, the data remains queryable and migratable, while rendering stays an application-level decision.

Coelho also deliberately keeps the core small: no file storage, image processing or collaborative editing. Those are integration points rather than responsibilities of the document layer.

It&apos;s early, but the core is already in place and tested.

I&apos;d particularly love feedback from people building Phoenix applications that need rich text, especially if you&apos;ve used Action Text or another structured rich text solution before.

HexDocs: https://coelho.hexdocs.pm/readme.html

Hex.pm: https://hex.pm/packages/coelho

https://github.com/nseaSeb/coelho</body_markdown>
  <bookmark_count>0</bookmark_count>
  <fediverse_like_count>0</fediverse_like_count>
  <fediverse_reactions/>
  <fediverse_reply_count>0</fediverse_reply_count>
  <fediverse_repost_count>0</fediverse_repost_count>
  <likers>
    <item>
      <name>Stefan Wintermeyer</name>
      <username>wintermeyer</username>
      <url>https://vutuv.de/wintermeyer</url>
    </item>
  </likers>
  <repost_count>1</repost_count>
  <thread_truncated>false</thread_truncated>
  <verified_author_links/>
</post>
