Server-rendered components

A Dstar.Component owns the stage UI and its event handlers. This page embeds it on first render; events dispatch straight to the component.

Demo

Variants

Component stage

renders: 0

profile

Ada Lovelace

Release coordinator · Dstar lab

render 0
Open commands
7
Patch latency
34 ms
Signal scope
tab

How it works

  • ComponentStage is a Dstar.Component: UI plus handle_event/3.
  • This page has no handle_event clauses for the stage at all.
  • event/2 targets /ds/<module>/<event> via dstar_components/2.
  • The same component renders on first paint and in SSE patches.

ComponentStage is a Dstar.Component: it owns both its markup and its handle_event/3. Events target /ds/<module>/<event>, so this page module has no handler clauses for the stage at all — and the same component function renders on first paint and inside every later patch.

lib/dstar_demo_web/pages/server_components_page.ex
<.action_button
  id="component-profile"
  label="Profile"
  icon="hero-identification-micro"
  click={"$component_kind = 'profile'; #{ComponentStage.event("profile")}"}
  active={@component_kind == "profile"}
  data-class={variant_classes("profile")}
/>

If this were LiveView

Both sides colocate the stage's markup with the handler that updates it, and both re-render the same function on first paint and on every update.

Dstar

lib/dstar_demo_web/pages/server_components_page.ex
<.action_button
  id="component-profile"
  label="Profile"
  icon="hero-identification-micro"
  click={"$component_kind = 'profile'; #{ComponentStage.event("profile")}"}
  active={@component_kind == "profile"}
  data-class={variant_classes("profile")}
/>

LiveView

illustrative
# Illustrative LiveView equivalent — not compiled or run.
defmodule DstarDemoWeb.ComponentStageLive do
  use DstarDemoWeb, :live_component

  def render(assigns) do
    ~H"""
    <div id={@id}>
      <button phx-click="profile" phx-target={@myself}>Profile</button>
      <button phx-click="timeline" phx-target={@myself}>Timeline</button>
      <button phx-click="deploy" phx-target={@myself}>Deploy</button>

      <p>{@component_kind} · renders: {@render_count}</p>
    </div>
    """
  end

  def handle_event(kind, _params, socket) do
    {:noreply,
     assign(socket,
       component_kind: kind,
       render_count: socket.assigns.render_count + 1
     )}
  end
end

# In the parent LiveView:
#
#   <.live_component
#     module={DstarDemoWeb.ComponentStageLive}
#     id="component-stage"
#     component_kind={@component_kind}
#     render_count={@render_count}
#   />