Skip to content

Add preview run mode#6663

Open
adhami3310 wants to merge 5 commits into
mainfrom
dev-build-mode
Open

Add preview run mode#6663
adhami3310 wants to merge 5 commits into
mainfrom
dev-build-mode

Conversation

@adhami3310

@adhami3310 adhami3310 commented Jun 16, 2026

Copy link
Copy Markdown
Member

What

Adds a new run mode: reflex run --env preview.

It hot reloads like dev, but instead of running the Vite dev server it serves a freshly built, un-minified frontend bundle mounted into the backend on a single port. Each backend hot reload re-runs the frontend build against the newly compiled output; a manual browser refresh shows changes.

The motivation: get dev-like iteration without the overhead of an always-running Vite server, while serving output much closer to a real production bundle (useful for reproducing "works in dev, breaks in prod" issues).

Note: unlike vite preview (which serves a static build with no rebuild), this mode still hot-reloads and rebuilds on each change.

How

  • New Env.PREVIEW value, surfaced through run --env (and excluded from export --env, which stays dev/prod).
  • _run_preview() reuses the existing prod static-mount path on a dev/reload backend — single process, no Vite.
  • On each hot reload, App.__call__ re-runs build.build() (gated on preview mode + the frontend being mounted), wrapped so a build failure keeps serving the previous build instead of taking the backend down.

To keep cold rebuilds fast and the output debuggable, preview flips a few defaults (all overridable via env vars):

Setting preview Mechanism
JS + CSS minify off VITE_MINIFYbuild.minify / build.cssMinify
autoprefixer off REFLEX_NO_AUTOPREFIXER, read by the generated postcss.config.js
sourcemaps off (default) VITE_SOURCEMAP

These came out of profiling: on a normal app the cold build is ~1.8s, dominated by toolchain startup, PostCSS/autoprefixer over the stylesheet, and react-router's SPA index.html step. The flips above shave ~0.3s (~15%) and land preview around ~1.5s.

Notes

  • The postcss.config.js autoprefixer toggle keeps the autoprefixer: entry on a single line so the tailwind v3/v4 config rewriters' line-based parsing stays valid; postcss-load-config disables a plugin whose value is false. Existing apps pick this up on their next reinit.
  • vite build --watch was evaluated as a faster alternative but rejected: it doesn't generate the SPA index.html and reintroduces a persistent process, defeating the goal.

Tests

  • Env round-trip (incl. preview), is_prod_mode() stays False in preview.
  • vite config emits minify/cssMinify from VITE_MINIFY.
  • ruff, pyright, and unit tests pass.

Follow-ups (not in this PR)

  • Docs entry for --env preview.

`reflex run --env dev-build` hot reloads like `dev`, but instead of running
the Vite dev server it serves a freshly built (un-minified) frontend bundle
mounted into the backend on a single port. Each backend hot reload re-runs the
frontend build against the newly compiled output; a manual browser refresh
shows changes. This trades the always-on Vite dev server's overhead for cold
rebuilds while staying close to dev semantics.

To keep rebuilds fast and output debuggable, dev-build disables (overridable):
- JS + CSS minification (VITE_MINIFY -> build.minify / build.cssMinify)
- autoprefixer (REFLEX_NO_AUTOPREFIXER, read by the generated postcss.config.js)
- sourcemaps stay off (the default)

Also threads VITE_MINIFY into the vite config, restricts `export --env` to
dev/prod, and fixes `run`'s env parsing to honor the new value.
@adhami3310 adhami3310 requested a review from a team as a code owner June 16, 2026 20:01
@FarhanAliRaza

Copy link
Copy Markdown
Contributor

We can maybe call it preview. Like other frontend frameworks.

@codspeed-hq

codspeed-hq Bot commented Jun 16, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing dev-build-mode (ff68e20) with main (932f20f)

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds reflex run --env preview, a new run mode that hot-reloads like dev but serves a freshly built, un-minified static frontend bundle mounted into the backend (single port, no Vite dev server). All three issues flagged in earlier review rounds — the autoprefixer truthiness bug, the unhandled build failure on hot reload, and the missing telemetry distinction — are addressed in the current HEAD.

  • _run_preview() mirrors _run_prod() but calls exec.run_backend() (with Granian hot-reload) instead of exec.run_backend_prod(), and sets env defaults (VITE_MINIFY=false, REFLEX_NO_AUTOPREFIXER=true) before spawning workers so they are inherited correctly.
  • On each hot reload, App.__call__ calls build.build() (gated on trigger == "hot_reload" + preview mode + frontend mounted) inside a try/except (Exception, SystemExit) so a failed build keeps the previous bundle alive.
  • export is restricted to DEV/PROD; the run CLI now passes Env(env) through unchanged instead of collapsing everything non-DEV to PROD.

Confidence Score: 5/5

Safe to merge — the new code path is cleanly isolated, all three previously flagged issues are fixed, and the existing dev/prod paths are unchanged.

The env-var propagation model is correct (vars are set in the main process before Granian spawns workers), the .nocompile file mechanism prevents double-compilation on first startup, and the hot-reload rebuild is guarded and failure-tolerant. No regressions to existing modes were introduced.

No files require special attention.

Important Files Changed

Filename Overview
reflex/reflex.py Adds _run_preview(), routes Env.PREVIEW through _run(), fixes the run CLI to pass env values through unchanged, and restricts export to DEV/PROD only. Logic is correct: env defaults are set before the backend starts, so workers inherit them; the single-port constraint is applied consistently with prod.
reflex/app.py Captures the compile trigger before _compile() and uses it to gate a preview-mode frontend rebuild on each hot reload; the build is wrapped in try/except so a failed build keeps serving the previous bundle. The REFLEX_MOUNT_FRONTEND_COMPILED_APP check ensures the rebuild only runs in fullstack preview, not backend-only or frontend-only.
packages/reflex-base/src/reflex_base/environment.py Adds VITE_MINIFY (default True) and REFLEX_NO_AUTOPREFIXER (default False) env vars without internal=True, so their names are consistent between Python and the Node.js processes that read them.
packages/reflex-base/src/reflex_base/.templates/web/postcss.config.js Adds a noVendorPrefix flag parsed with a case-insensitive regex so that REFLEX_NO_AUTOPREFIXER=false/0/empty correctly keeps autoprefixer enabled. The autoprefixer: entry stays on a single line to keep the tailwind v3/v4 config rewriters working.
packages/reflex-base/src/reflex_base/compiler/templates.py Adds minify and cssMinify to the vite build config template, correctly emitting "true" or "false" based on the minify parameter; both JS and CSS minification are tied to the same flag.
tests/units/utils/test_utils.py Adds four well-scoped tests: preview mode is not prod, Env enum round-trips correctly, the vite config template emits the right minify/cssMinify values, and _compile_vite_config reads VITE_MINIFY from the environment.

Reviews (4): Last reviewed commit: "preview: drop internal=True on REFLEX_NO..." | Re-trigger Greptile

Comment thread packages/reflex-base/src/reflex_base/.templates/web/postcss.config.js Outdated
Comment thread reflex/app.py Outdated
Comment thread reflex/reflex.py Outdated
- app.py: guard build.build() on hot reload so a failed frontend build keeps
  serving the previous build instead of taking the backend down.
- postcss.config.js: make REFLEX_NO_AUTOPREFIXER honor falsy values ("false",
  "0", "") so the override actually works; keep the autoprefixer entry on a
  single line for the tailwind config rewriters.
- Send a distinct "run-dev-build" telemetry event.
- Add news fragments for reflex and reflex-base.
Per review feedback, align the new run mode's name with the frontend-ecosystem
convention (Vite/Next/Nuxt 'preview').
@adhami3310 adhami3310 changed the title Add dev-build run mode Add preview run mode Jun 16, 2026
@adhami3310

Copy link
Copy Markdown
Member Author

Good call — renamed the mode to preview (reflex run --env preview) in 4a391dd to match the Vite/Next/Nuxt convention. One nuance worth flagging for docs: unlike vite preview (which just serves a static build), this mode still hot-reloads and rebuilds on every change.

Comment thread packages/reflex-base/src/reflex_base/environment.py Outdated
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.

2 participants