Skip to content

Explicit state and event id minification#6100

Open
benedikt-bartscher wants to merge 35 commits intoreflex-dev:mainfrom
benedikt-bartscher:explicit-event-id-minification
Open

Explicit state and event id minification#6100
benedikt-bartscher wants to merge 35 commits intoreflex-dev:mainfrom
benedikt-bartscher:explicit-event-id-minification

Conversation

@benedikt-bartscher
Copy link
Contributor

@benedikt-bartscher benedikt-bartscher commented Jan 24, 2026

Summary

Add state and event name minification with CLI management tools.

  • Introduces minify.json config file to map state/event names to short IDs (e.g., myapp.state.AppState"a")
  • Adds reflex minify CLI command group for managing minification
  • Reduces frontend bundle size and WebSocket payload size by replacing long state/event paths with compact identifiers

CLI Commands

# Initialize minify.json with IDs for all states and events
reflex minify init

# Sync minify.json after code changes
reflex minify sync
reflex minify sync --prune --reassign-deleted

# Validate config against current codebase
reflex minify validate

# List state tree with minified names
reflex minify list
reflex minify list --json

# Lookup a minified path
reflex minify lookup "a.bU"

based on #6098

@codspeed-hq
Copy link

codspeed-hq bot commented Jan 24, 2026

Merging this PR will not alter performance

✅ 8 untouched benchmarks


Comparing benedikt-bartscher:explicit-event-id-minification (dbb1fb6) with main (88155a7)

Open in CodSpeed

@benedikt-bartscher benedikt-bartscher changed the title Explicit event id minification Explicit state and event id minification Jan 25, 2026
has_substates = len(substates) > 0

if handlers:
console.log(f"{child_prefix}|-- Event Handlers:")
Copy link
Contributor

@abulvenz abulvenz Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very tiny optical nit, cause sometimes EventHandlers is the last sub item.
It might work like this, didn't test it.

Suggested change
console.log(f"{child_prefix}|-- Event Handlers:")
console.log(f"{child_prefix}{'|' if has_substates else '`'}-- Event Handlers:")

@benedikt-bartscher
Copy link
Contributor Author

benedikt-bartscher commented Jan 28, 2026

TODO: best-effort-mode

  • walk trough the state id registry after the app module get's imported
  • filter out all states with explicit state id
  • sort the remaining states without state id by full name
  • assign state ids - start with the highest user defined state id - users might have fragmentation by choice

a similar thing can be implemented for event handler id's

edit: not that easy, as state names are baked into to many things (events, vars etc). we would need to defer init_subclass and possibly parts of @rx.var and @rx.event

edit2: i have found a better approach with a minify.json which makes this obsolete

@benedikt-bartscher
Copy link
Contributor Author

benedikt-bartscher commented Jan 28, 2026

Open for discussion: Should reflex reserve the first 5/10 state id's for internal states? Current reflex uses 0-3, but that might change in the future.

edit: with sibling uniqueness we would just need to reserve the state id's on the first rx.State subclass level.

edit2: i have found a better approach with a minify.json which makes this obsolete

@benedikt-bartscher
Copy link
Contributor Author

benedikt-bartscher commented Jan 28, 2026

TODO: think about sibling uniqueness for states. currently using global uniqueness

edit: adjusted in 6b4c5fa

@benedikt-bartscher benedikt-bartscher force-pushed the explicit-event-id-minification branch from 35ad1c6 to 1647c1e Compare February 1, 2026 10:25
@benedikt-bartscher
Copy link
Contributor Author

Idea: store ids in a minify.json file

  • allows overwriting state ids for upstream dependencies and reflex core
  • one centralized place for the ids
  • keep track of reserved numbers
  • better automatic instrumentation - state and event ids can all be managed with cli - without manual code changes
  • can be loaded once on startup and is frozen - probably faster
  • no best effort mode needed

When a parent and child state have the same minified name, substate
resolution can fail because the leading segment is stripped incorrectly. This
change adds a flag to skip stripping only on the initial recursive call, ensuring
correct resolution even in name collision scenarios.
Comment on lines -245 to -248
fill: Var[str | Color]

# The stroke color of brush
stroke: Var[str | Color]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

@benedikt-bartscher benedikt-bartscher marked this pull request as ready for review February 5, 2026 23:50
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 5, 2026

Greptile Overview

Greptile Summary

This change updates the reflex minify CLI output to correctly count event handlers now that minify.json stores events nested under each state path (summing handler counts rather than using the top-level dict length).

The minify command group lives in reflex/reflex.py alongside other CLI groups and interacts with the broader minification system by loading the app (to register State subclasses) and then calling reflex.minify helpers to generate/sync/validate configurations.

Confidence Score: 3/5

  • This PR has one functional issue in a user-facing CLI subcommand that should be fixed before merge.
  • The event counting fix is straightforward, but reflex minify lookup appears to call State.get_class_substate() with a single segment even though it expects a full dotted path, which will break nested lookups.
  • reflex/reflex.py

Important Files Changed

Filename Overview
reflex/reflex.py Fixes minify CLI event counting; minify_lookup substate resolution appears incorrect for multi-segment paths.
.gitignore Adds uploaded_files/ to ignore list.
pyi_hashes.json Updates generated stub hash entries.

Sequence Diagram

sequenceDiagram
  autonumber
  participant User
  participant CLI as reflex/reflex.py (click)
  participant Prereq as reflex.utils.prerequisites
  participant State as reflex.state.State
  participant Minify as reflex.minify
  participant FS as minify.json

  User->>CLI: reflex minify sync/validate/init
  CLI->>Prereq: get_app() (register State subclasses)
  Prereq->>State: import app / build state tree
  CLI->>Minify: generate/sync/validate config
  Minify->>FS: read/write minify.json
  Minify-->>CLI: config/errors/warnings
  CLI-->>User: log counts/results
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link
Collaborator

@masenf masenf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just started reviewing this, will need to test on it a bit more.

the explicit management of the minified names via cli solves our earlier problems of mismatching the minified names between compile and build time, but it seems like a lot of extra api surface area for a mostly under the covers feature.

would it make sense to auto-minify and store the minify.json as a compilation artifact in the .web/backend directory? we already do something similar to save which pages need to be re-evaluated in backend-only mode in order to have a consistent state tree.

Comment on lines 458 to 460
assert isinstance(handler, EventHandler), (
f"Expected EventHandler, got {type(handler)}"
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertions in non-test code might be optimized out if one runs with python -O

since this runs as part of the compiler, it's better to check the condition and (in this case) raise TypeError

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is mainly used to satisfy the type checker. A pattern which i personally prefer over cast. At runtime this should always be EventHandler. However, no strong preference, i just changed it to raise instead

raise SystemExit(1)

# Load the user's app to register all state classes
prerequisites.get_app()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just getting the app might not be sufficient to slurp all of the states. some states, like ComponentState or dynamically created states might only be created while evaluating pages.

maybe it's okay to leave those out of the minification?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, i was aware of this limitation, maybe i can figure it out later. I'd love to support ComponentState as well

benedikt-bartscher added a commit to benedikt-bartscher/reflex that referenced this pull request Feb 12, 2026
@benedikt-bartscher
Copy link
Contributor Author

Thanks you @masenf!

the explicit management of the minified names via cli solves our earlier problems of mismatching the minified names between compile and build time, but it seems like a lot of extra api surface area for a mostly under the covers feature.

Yeah, it is. Advanced users and big deployments might prefer managing this manually. I think about it like db migrations. Manual management makes it possible to rename states and event handlers without any issues in production.

would it make sense to auto-minify and store the minify.json as a compilation artifact in the .web/backend directory? we already do something similar to save which pages need to be re-evaluated in backend-only mode in order to have a consistent state tree.

The minify.json should be persisted/commited to have stable id's. However if we decide that minify becomes default, we can certainly auto-update the minify.json during reflex run and/or reflex compile

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.

3 participants