Tiptapex.Renderer (Tiptapex v0.1.0)

Copy Markdown View Source

Renders Tiptap/ProseMirror JSON documents to safe HTML on the server.

{:safe, _} = Tiptapex.Renderer.to_html(article.body)

In HEEx just interpolate it — no raw/1 needed, and none should ever be used with client-provided HTML:

<div class="ttx-prose">{Tiptapex.Renderer.to_html(@article.body)}</div>

All text is escaped, attributes go through an allow-list per node, URLs must be http(s)/mailto/relative, iframes are restricted to rebuilt YouTube embed URLs, and inline CSS values must match a strict grammar (see Tiptapex.Renderer.URL). Unknown node/mark types are dropped by default.

Options

  • :nodes — map of "type" => renderer merged over the built-ins, where renderer is a (node, children_iodata, opts) -> iodata fun or a module implementing Tiptapex.Renderer.Node.
  • :marks — same for marks: (mark, children_iodata, opts) -> iodata.
  • :on_unknown — what to do with unknown node/mark types: :drop (default), :keep_children, or :raise.
  • :ids — whether to emit heading id attributes (default true). Pass false whenever the rendered HTML appears on the same page as a live editor showing the same document (e.g. a preview panel): the editor's DOM carries the same ids, and LiveView's DOM patcher matches elements by id globally — it will steal nodes out of the editor (even inside phx-update="ignore"), deleting them from the document.

Custom nodes

defmodule MyApp.CalloutNode do
  @behaviour Tiptapex.Renderer.Node
  alias Tiptapex.Renderer.HTML

  @impl true
  def render(_node, children, _opts) do
    HTML.tag("aside", [{"class", "callout"}], children)
  end
end

Tiptapex.Renderer.to_html(doc, nodes: %{"callout" => MyApp.CalloutNode})

Summary

Functions

Lists the document's headings as %{level: 1..6, id: binary | nil, text: binary} — ready to build a table of contents. Ids come from the UniqueID extension when present.

Converts a Tiptap JSON document (map, string or atom keys) to {:safe, iodata}.

Extracts the document's plain text — for excerpts, search indexing, or emptiness checks. Blocks are separated by newlines; media nodes are skipped.

Functions

headings(doc)

@spec headings(map() | nil) :: [%{level: 1..6, id: binary() | nil, text: binary()}]

Lists the document's headings as %{level: 1..6, id: binary | nil, text: binary} — ready to build a table of contents. Ids come from the UniqueID extension when present.

to_html(doc, opts \\ [])

@spec to_html(
  map() | nil,
  keyword()
) :: Phoenix.HTML.safe()

Converts a Tiptap JSON document (map, string or atom keys) to {:safe, iodata}.

to_plain_text(doc)

@spec to_plain_text(map() | nil) :: String.t()

Extracts the document's plain text — for excerpts, search indexing, or emptiness checks. Blocks are separated by newlines; media nodes are skipped.