Skip to content

feat(dev): hot-reload Vuetify config under SSR without a dev-server restart#380

Merged
AndreyYolkin merged 19 commits into
mainfrom
feat/ssr-config-hot-reload
Jun 14, 2026
Merged

feat(dev): hot-reload Vuetify config under SSR without a dev-server restart#380
AndreyYolkin merged 19 commits into
mainfrom
feat/ssr-config-hot-reload

Conversation

@AndreyYolkin

@AndreyYolkin AndreyYolkin commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Main idea

In dev, editing a Vuetify config file under SSR triggered a full nuxt.callHook('restart'). This makes those edits hot-reload instead — the SSR output refreshes and the open browser auto-reloads, with no dev-server restart — on Nuxt ≥ 3.18 (the Vite 7 line + all of Nuxt 4). Nuxt 3.15–3.17 (Vite 6) keep the restart fallback.

The config is exposed as Vite virtual modules executed by Nuxt's vite-node SSR runner, whose cache the usual invalidation APIs don't evict. The fix gives that runner a real dependency edge to act on: the virtual config module emits a dev-SSR-only side-effect import of the config file (so vite-node cascades a config edit → virtual module → server entry and re-evaluates it). On change we re-read the config into ctx, invalidate the SSR transforms, and broadcast a client full-reload. load() gained a reload flag that skips re-applying nuxt.options (avoids a Nitro dev:reload + a latent duplicate-head/css bug). A supportsSsrConfigHmr gate (floor 3.18.0) selects HMR vs. the restart fallback.

Scope: hot-reloads theme, defaults, components, aliases, directives, locale messages. Icon-set / date-adapter and icon CDN/<head> changes still need a restart (documented). The mechanism leans on @nuxt/vite-builder vite-node internals; safe restart fallback below Nuxt 3.18.

Verified: version-gate unit tests; a dev-SSR e2e asserting no-restart (boot-count probe); and the runner-eviction confirmed end-to-end on apps/playground at both Nuxt 3.21.8 and 4.0.0 with a fix-out/fix-in control (pre-fix stays stale).

References

…config

The previous approach (addWatchFile + handleHotUpdate reload) worked only on
tiny SSR module graphs (the e2e fixture) but not real apps: the vite-node SSR
runner never re-executed the virtual config module, so SSR served stale config.

Emit a dev-SSR-only side-effect import of the config sources from the virtual
config module so the runner records a config-file -> virtual-module dependency
edge; adding the config files to the client watcher then makes Nuxt's vite-node
invalidate set cascade through that edge to the server entry on edit. Re-read
the config and invalidate the SSR transforms inside an awaited handleHotUpdate
returning [] (no full-reload, which would suspend SSR for non-browser requests).

Verified against the real playground (SSR): theme edit reflected in ~0.7s with
no dev-server restart.
… scope

Address final code review: make the icons/date SSR-eviction gap explicit (no
import edge -> not hot-reloaded under SSR, needs restart) in code comments and
the docs caveat; clarify that addWatchFile alone does not evict the SSR runner
(the dev-SSR import edge does); note the e2e guards no-restart + wiring but not
the eviction mechanism; mark the 4.3.0 floor as a conservative inference.
The required @nuxt/vite-builder internals (useInvalidates/markInvalidate/
invalidateDepTree, environments.ssr.fetchModule) and the Vite Environment API
(every Nuxt 4.x uses Vite 7) are present since 4.0.0 — verified by inspecting
the published 4.0.0-4.3.1 dists and confirmed end-to-end on apps/playground
running Nuxt 4.0.0 (config edit hot-reloaded, no dev-server restart). The 4.3.0
floor was an overly conservative untested guess. Nuxt 3 still uses the restart
fallback.
The Vite 7 + Environment-API invalidation mechanism the fix relies on landed in
@nuxt/vite-builder 3.18.0 (3.x line) and 4.0.0 (4.x line); Nuxt 3.15-3.17 ship
Vite 6 with the older mechanism and don't qualify. Confirmed end-to-end on
apps/playground at both Nuxt 3.21.8 and 4.0.0 (config edit hot-reloaded, no
restart; pre-fix build stayed stale — valid control). Nuxt 3.15-3.17 keep the
restart fallback.
…vironmentApi

The previous code only captured the dedicated SSR dev server (flag off). With
experimental.viteEnvironmentApi enabled there's a single server and that event
never fires, so SSR config edits silently went stale. Capture the SSR graph
from server.environments.ssr in that case too. Verified on apps/playground with
the flag both off and on (config edit hot-reloaded in ~1s, no restart).
@AndreyYolkin AndreyYolkin merged commit b07d647 into main Jun 14, 2026
2 checks passed
@AndreyYolkin AndreyYolkin deleted the feat/ssr-config-hot-reload branch June 14, 2026 16:00
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