Reusable markdown editor LiveComponent with cursor tracking, markdown formatting toolbar, component insertion, and unsaved changes protection.
Features
- Monospace textarea optimized for markdown/code editing
- Markdown formatting toolbar with selection-aware text manipulation
- Cursor position tracking for inserting components at cursor
- Optional toolbar for inserting images, videos, etc.
- Save status indicator (saving/unsaved/saved)
- Browser navigation protection for unsaved changes
- Debounced content updates
Usage
<.live_component
module={PhoenixKitWeb.Components.Core.MarkdownEditor}
id="content-editor"
content={@content}
save_status={@save_status}
show_formatting_toolbar={true}
toolbar={[:image, :video]}
on_change="content_changed"
on_save="save_content"
on_insert_component="insert_component"
/>Formatting Toolbar
The formatting toolbar includes:
- Headings: H1-H6 (adds
#prefix to current line) - Inline styles: Bold, Italic, Strikethrough, Inline Code (wraps selection)
- Links: Prompts for URL and wraps selection as link text
- Lists: Bullet and numbered lists (adds prefix to current line)
When text is selected, formatting wraps the selection. When no text is selected, a placeholder is inserted and auto-selected for easy replacement.
Events Sent to Parent
The component sends messages to the parent LiveView via send/2:
{:editor_content_changed, %{content: content, editor_id: id}}- Content updated{:editor_insert_component, %{type: :image | :video, editor_id: id}}- Toolbar button clicked{:editor_save_requested, %{editor_id: id}}- Save button clicked
Commands from Parent
Use send_update/2 to send commands to the component:
# Insert text at cursor position
send_update(PhoenixKitWeb.Components.Core.MarkdownEditor,
id: "content-editor",
action: :insert_at_cursor,
text: "<Image file_id=\"abc123\" />"
)CSP Nonce
If your app uses Content Security Policy, pass the nonce:
<.live_component
module={PhoenixKitWeb.Components.Core.MarkdownEditor}
id="editor"
content={@content}
script_nonce={assigns[:csp_nonce] || ""}
/>