PhoenixStreamdown
Streaming markdown renderer for Phoenix LiveView, optimized for LLM output.
Inspired by Streamdown and vue-stream-markdown, but built to fully leverage LiveView — server-side rendering, automatic DOM diffing, word-level streaming animations, and zero client-side JavaScript.
Installation
def deps do
[{:phoenix_streamdown, "~> 1.0.0-beta"}]
endUsage
use PhoenixStreamdown<.markdown content={@response} streaming={@streaming?} />
That's it. Pass content as a markdown string, set streaming to true while tokens are arriving. Completed blocks are frozen with phx-update="ignore" — only the last block re-renders on each token.
Streaming animations
Add word-level fade-in like Vercel's Streamdown:
<.markdown content={@response} streaming animate="fadeIn" />
Include the CSS (fadeIn, blurIn, slideUp):
@import "../../deps/phoenix_streamdown/priv/static/phoenix_streamdown.css";How it works
- Remend — auto-closes incomplete syntax (
**bold→**bold**, unclosed fences, partial links) - Blocks — splits into independent blocks so earlier ones are stable
- MDEx — renders each block to HTML server-side (Rust-backed)
- LiveView — diffs only the active block, skips the rest
On a 56-block document, this is ~7x less server work and ~460x smaller diffs per token compared to re-rendering the full document each time.
Example
A full chat app with ReqLLM streaming is in the example/ directory. Run it:
cd example
cp .env.example .env # add your OpenRouter API key
mix setup
mix phx.serverDocumentation
HexDocs — attributes, customization (themes, CSS classes, stable IDs, MDEx options).
License
MIT