HEEx function components for the Tiptap editor and viewer.
import Tiptapex.Components
<.tiptapex_editor id="body" value={@doc} upload_url={~p"/tiptapex/uploads"} />
<.tiptapex_viewer id="article" value={@article.body} />Both components render a phx-update="ignore" root owned by the JS hooks
shipped in this package (TiptapexEditor / TiptapexViewer); register the
hooks in your app.js (see the Tiptapex module docs).
Sync modes
The editor supports two ways of getting the document back to the server:
sync={:push_event}(default) — the hook pushes the configuredon_changeevent (default"tiptapex_change") with%{"json" => doc, "html" => html, "characters" => n}on every debounced change. Handle it in your LiveView and persist on an explicit save action. Do not trust the pushed"html"for display — re-render the JSON server-side withTiptapex.Renderer.to_html/2.sync={{:hidden_input, @form[:body]}}— the component renders a hidden input bound to the form field and the hook keeps it in sync with the document JSON, dispatching aninputevent so the form'sphx-changefires. Pair the field withTiptapex.Schema.Documentin your Ecto schema and no customhandle_eventis needed.
Summary
Functions
Pushes new content into a mounted editor from the server.
Renders the Tiptap editor.
Renders a Tiptap document read-only.
Functions
Pushes new content into a mounted editor from the server.
{:noreply, Tiptapex.Components.set_content(socket, "body", restored_doc)}The event is namespaced by the editor's id, so multiple editors in one
LiveView never receive each other's content.
Renders the Tiptap editor.
Examples
<.tiptapex_editor id="body" value={@body} on_change="editor_update" />
<.tiptapex_editor
id="body"
value={@body}
upload_url={~p"/admin/uploads"}
upload_scope={@article.id}
collab={%{topic: "doc:" <> @article.slug, user: %{id: 1, name: "Ada", color: "#309"}}}
toolbar={[:marks, :blocks, :lists, :history]}
extensions={%{table: false, character_count_limit: 10_000}}
/>Attributes
id(:string) (required) - logical editor id; also namespaces its events.value(:map) - the Tiptap/ProseMirror JSON document (map or nil). Defaults tonil.placeholder(:string) - Defaults tonil.upload_url(:string) - endpoint for editor uploads; nil disables uploads entirely. Defaults tonil.upload_scope(:any) - opaque value POSTed with each upload (e.g. the record id). Pass nil to declare "scope required but record not saved yet" — uploads stay blocked until it has a value. Leave as :none for unscoped uploads.Defaults to
:none.upload_scope_name(:string) - form field name for the scope. Defaults to"scope".collab(:map) - enables realtime collaboration: %{topic: "...", socket_path: "/socket", user: %{id: ..., name: ..., email: ..., color: ...}}. Requires the host to build its hook with the CollabPlugin (see the collaboration guide).Defaults to
nil.toolbar(:any) - nil for the full default toolbar, false to hide it, a list of group atoms (:marks, :blocks, :align, :lists, :typography, :colors, :insert, :utilities, :history) for an ordered subset, or a map for full config.Defaults to
nil.extensions(:map) - per-feature switches forwarded to the JS buildExtensions/1, e.g. %{table: false, drag_handle: false, character_count_limit: 10_000}.Defaults to
nil.labels(:map) - i18n label overrides for toolbar and table menu. Defaults tonil.remount_key(:any) - bump this value to force a full client remount (e.g. after restoring a version). Defaults tonil.on_change(:string) - event pushed on debounced updates (default "tiptapex_change"). Defaults tonil.on_uploaded(:string) - event pushed after a successful upload (default "tiptapex_uploaded"). Defaults tonil.sync(:any) - :push_event or {:hidden_input, form_field}. Defaults to:push_event.debounce(:integer) - debounce for change events, in ms (default 400). Defaults tonil.count_template(:string) - footer counter template, e.g. "{chars} caracteres · {words} palabras". Defaults tonil.class(:any) - Defaults tonil.Global attributes are accepted.
Slots
actions- rendered in the editor footer, next to the character count.
Renders a Tiptap document read-only.
Server-side it renders safe HTML via Tiptapex.Renderer.to_html/2 — no
raw/1, no trust in client HTML. With hydrate (the default) the
TiptapexViewer hook replaces that fallback with a client-side Tiptap
render, which reproduces interactive niceties (task list checkboxes,
identical NodeViews) — visually both are the same.
Attributes
id(:string) - required when hydrate is true. Defaults tonil.value(:map) - the Tiptap/ProseMirror JSON document. Defaults tonil.hydrate(:boolean) - Defaults totrue.class(:any) - Defaults tonil.renderer(:list) - options forwarded to Tiptapex.Renderer.to_html/2. Defaults to[].- Global attributes are accepted.