Props & data

Props Diffing

Only the changed path travels, unless a component opts out

  • data-props-diff
  • diff={false}

Key concepts

By default LiveReact sends only the part of a prop that changed, not the whole value. Both components below share one large payload assign; clicking the button changes a single field inside it. Only the changed path travels to the diffed instance — the other resends the entire payload on every update, because it opted out.

props_diffing_live.ex
defmodule MyAppWeb.PropsDiffingLive do

  use MyAppWeb, :live_view

  def mount(_params, _session, socket) do
    {:ok, assign(socket, payload: build_payload(0))}
  end

  def render(assigns) do
    ~H"""
    <div class="space-y-4">
      <.button phx-click="touch_one_field">Change one field</.button>

      <p class="text-sm text-[color:var(--text-muted)]">
        Each instance reports how many bytes actually changed on its wrapper element
        for the last update, and the running total. The diffed instance receives a
        small <code>data-props-diff</code>
        patch; the other receives the whole <code>data-props</code>
        payload again every time.
      </p>

      <.react name="PropsDiffing" label="diff={true}" payload={@payload} socket={@socket} />

      <.react
        name="PropsDiffing"
        label="diff={false}"
        payload={@payload}
        diff={false}
        socket={@socket}
      />
    </div>
    """
  end

  def handle_event("touch_one_field", _params, socket) do
    {:noreply, assign(socket, payload: Map.update!(socket.assigns.payload, :counter, &(&1 + 1)))}
  end

  defp build_payload(counter) do
    %{
      counter: counter,
      rows: Enum.map(1..40, &%{id: &1, name: "row #{&1}", note: String.duplicate("x", 40)})
    }
  end
end

How it works

LiveReact writes data-use-diff on the component's root element to say which mode it is in. When diffing is on, prop changes are computed as a JSON Patch and written to data-props-diff; the client applies that patch to the props it already has instead of receiving a new snapshot.

Diffing is on by default, controlled globally by config :live_react, :enable_props_diff. Pass diff={false} on a single <.react> call to opt that instance out and always send the full prop value — useful when a prop is small enough that diffing is pure overhead, or when a component needs the complete value on every render.