Functions for patching and removing DOM elements via SSE.
conn |> patch("<div>New content</div>", selector: "#target")
conn |> patch("<p>Inner</p>", selector: "#target", mode: :inner)
conn |> remove("#target")
Summary
Functions
Appends html as the last child of container.
Formats an element patch as an SSE event string (for stateless responses).
Formats an element removal as an SSE event string (for stateless responses).
Patches DOM elements with new HTML content.
Removes elements from the DOM by selector.
Morphs the element whose DOM id matches the root element of html.
Functions
@spec append(Plug.Conn.t(), String.t() | Phoenix.HTML.safe(), String.t(), keyword()) :: Plug.Conn.t()
Appends html as the last child of container.
The fast path for create-events on a plain, unfiltered feed. If the
list is filtered, sorted or paginated, the stream cannot know this tab's
view state — use Dstar.Signals.nudge/3 instead. See the
Live collections guide.
Example
conn |> Dstar.Elements.append(post_row(%{post: post}), "#posts")
@spec format_patch( String.t() | Phoenix.HTML.safe() | nil, keyword() ) :: String.t()
Formats an element patch as an SSE event string (for stateless responses).
Example
format_patch("<div id=\"feed\">content</div>")
format_patch("<div>content</div>", selector: "#target")
format_patch("<circle r='10'/>", selector: "#svg", namespace: :svg)
Formats an element removal as an SSE event string (for stateless responses).
Example
format_remove("#target")
# => "event: datastar-patch-elements\ndata: mode remove\ndata: selector #target\n\n"
@spec patch(Plug.Conn.t(), String.t() | Phoenix.HTML.safe() | nil, keyword()) :: Plug.Conn.t()
Patches DOM elements with new HTML content.
When no :selector is provided, each top-level element in the HTML
must have an id attribute so Datastar can target it by ID.
The html argument may be nil when using mode: :remove (elements
are not needed for removal).
Options
:selector- CSS selector for target elements (optional — defaults to element ID):mode- Patch mode (default: :outer). Only non-default values are sent.:namespace- Element namespace::html,:svg,:mathml(default: :html):use_view_transitions- Enable View Transitions API (default: false):event_id- Event ID for client tracking:retry- Retry duration in milliseconds
Examples
# Patch by element ID (no selector needed)
conn |> patch("<div id=\"feed\">New content</div>")
# Patch with explicit selector
conn |> patch("<div>Content</div>", selector: "#target")
# Update inner HTML only
conn |> patch("<p>New text</p>", selector: ".content", mode: :inner)
# Append to element
conn |> patch("<li>Item</li>", selector: "ul", mode: :append)
# Remove by selector (no HTML needed)
conn |> patch(nil, selector: "#old", mode: :remove)
# SVG namespace
conn |> patch("<circle cx='50' cy='50' r='40'/>", selector: "#svg", namespace: :svg)
# With view transitions
conn |> patch("<div>Smooth</div>", selector: "#box", use_view_transitions: true)
@spec remove(Plug.Conn.t(), String.t(), keyword()) :: Plug.Conn.t()
Removes elements from the DOM by selector.
Sends a datastar-patch-elements event with mode remove.
Options
:event_id- Event ID for client tracking:retry- Retry duration in milliseconds
Example
conn
|> Dstar.Elements.remove(".temporary")
|> Dstar.Elements.remove("#old-content")
@spec upsert(Plug.Conn.t(), String.t() | Phoenix.HTML.safe(), keyword()) :: Plug.Conn.t()
Morphs the element whose DOM id matches the root element of html.
Sends no selector, so Datastar targets by id.
Dropped when absent
If the tab has no element with that id, Datastar logs
PatchElementsNoTargetsFound and drops the patch — a row this tab never
rendered stays absent. That is fine for a plain feed; when it is not
(filtered, sorted or paginated lists), use Dstar.Signals.nudge/3.
Example
conn |> Dstar.Elements.upsert(post_row(%{post: post}))