ReviewOS

stacks/stx

No way to revalidate server-rendered content after a mutation — invalidate() only re-runs client fetches, so apps fall back to location.reload()

#1850
Closed glennmichael123 opened this 10 days ago ·
10 days ago

After a mutation succeeds, there is no way to re-render server-rendered content. The only option is window.location.reload() — a full document load, losing scroll, focus and every client signal on the page.

Why invalidate() does not cover it

useQuery(...).invalidate() (packages/stx/src/signals.ts:1292) re-runs a client-side JSON fetch. That works when the list came from JSON. It cannot re-run a <script server> block, which is where an SSR-first app puts its data:

<script server>
const projects = await db.unsafe('SELECT … WHERE owner_id = $1', [user.id])
</script>

Delete a project via useMutation, and nothing can make that query run again.

The router's public API has no equivalent — packages/router/src/client.ts:1362-1372:

var router = { navigate, navigateTo, prefetch, clearCache, cache, swap, updateNav }

navigate(currentUrl) early-returns on the same URL (client.ts:509), and fragment navigation swaps the whole <main> (client.ts:931) rather than a named region.

What that costs

Four mutations in a real app end with window.location.reload() for exactly this reason — settings.stx:326, :460, :572, :617. Each one is a successful in-app action that drops the user to a cold page load.

What peers provide

frameworkAPIgranularity
Inertiarouter.reload({ only: ['projects'] })named props
NuxtrefreshNuxtData() / refresh() from useAsyncDataper-key
Remixautomatic loader revalidation after an actionroute loaders
Livewirecomponent re-render on actioncomponent

The common shape is "re-run the server data for this view, patch the DOM, keep client state". stx has the transport for it already — the fragment path fetches a rendered fragment and swaps it.

Suggested direction

The cheapest useful version is a router method that re-fetches the CURRENT url through the existing fragment path and swaps, bypassing the same-URL early return:

router.refresh()            // re-fetch current url, swap <main>, keep scroll

That alone replaces all four location.reload() calls. The early-return at client.ts:509 already has a force parameter (client.ts:481) — this is close to exposing what exists.

A more precise version worth considering separately is a named region — <div data-stx-refresh="projects"> plus router.refresh('projects') — so a mutation can re-render a list without disturbing the rest of the page. That is the Inertia only: model and it is what makes the pattern feel instant rather than merely correct.

Related: navigate() cannot express same-URL re-navigation either. The router supports it via a third force argument, but navigate() forwards only { replace } (signals.ts:1062) and StxNavigateOptions has no force. An app that writes a preference cookie and needs the same URL re-rendered has no expressible way to ask.

10 days ago

Claiming this one — working on it now.

Sign in to comment on this issue.