Skip to content

Let Volt render pages, and re-render islands on new props - #14

Merged
dannote merged 3 commits into
masterfrom
tab-documents
Oct 5, 2026
Merged

dannote merged 3 commits into
masterfrom
tab-documents

Conversation

@dannote

@dannote dannote commented Oct 3, 2026

Copy link
Copy Markdown
Member

Companion to elixir-volt/volt#54. It has no effect until a Volt release after 0.20.0; Volt 0.20.0 ignores the new option, and mix ci passes against it.

Changes

  • Render function for Volt. Astral.DevServer passes Volt document: {Astral.DevServer, :render_document, [config]}, which renders the HTML a page request is answered with. With Volt's morph setting, Volt's websocket process for each open page renders the page again when sources change and tells the page what changed, instead of the page requesting itself.

  • Islands re-render on new props. When the server rendered an island with new props, Volt sets them on the island's element and dispatches volt:element-update. Every adapter handles it through a shared onPropsUpdate helper:

    Framework How State
    Vue props held in a shallowRef kept
    React renders again on the same root kept
    Solid props read through a signal kept
    Svelte unmounts and mounts again reset

    Props passed to Svelte's mount are not reactive outside a Svelte module, which is why Svelte remounts.

Verification

  • mix ci passes: 257 tests, including four new browser tests, one per framework, that update an island's props and check its state.
  • A test checks that render_document/2 produces the HTML whose entity tag the served page carries.
  • Tried in a real browser on a copy of an Astral site against the Volt branch: renaming a heading updated the page and its Vue Contents island in place, with no reload.

Open points

  • Solid only tracks the props an island was mounted with.
  • Astral does not turn morph on; sites opt in with config :volt, :server, morph: [preserve: "[data-astral-island]"].

Astral.DevServer passes Volt a function that renders the HTML a page
request is answered with. With Volt's morph setting, Volt's websocket
process for each open page renders the page again when sources change
and tells the page what changed, instead of the page requesting itself.
Volt 0.20.0 ignores the option.

When the server rendered an island with new props, Volt sets them on the
island's element and dispatches volt:element-update. The Vue adapter
handles it by swapping its props, so the island re-renders in place and
keeps its state. Other adapters do not handle it yet, so the page
reloads for them.
Every adapter now handles volt:element-update through a shared
onPropsUpdate helper. React renders the component again on the same
root, and Solid reads props through a signal, so both keep the island's
state, as Vue does. Props passed to Svelte's mount are not reactive
outside a Svelte module, so a Svelte island is mounted again and its
state resets.
Volt 0.21 has the :document option and the volt:element-update event
that this branch uses, so the render function and island re-rendering
take effect. The basic example's lockfile follows.
@dannote
dannote merged commit e65dd5c into master Oct 5, 2026
@dannote dannote mentioned this pull request Oct 5, 2026
@dannote
dannote deleted the tab-documents branch October 5, 2026 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant