Bind widgets to query params - Part 3#13683
Bind widgets to query params - Part 3#13683mayagbarnes wants to merge 9 commits intoquery-param-bind-2from
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
✅ PR preview is ready!
|
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
📈 Frontend coverage change detectedThe frontend unit test (vitest) coverage has increased by 0.0800%
🎉 Great job on improving test coverage! |
f453c36 to
64c3a61
Compare
📉 Python coverage change detectedThe Python unit test coverage has decreased by 0.0033%
✅ Coverage change is within normal range. Coverage by files
|
369efb1 to
a3f6a97
Compare
573d177 to
c89191b
Compare
a3f6a97 to
58e746c
Compare
There was a problem hiding this comment.
Pull request overview
Adds bind="query-params" support for st.checkbox, st.toggle, and st.color_picker, enabling two-way synchronization between widget values and URL query parameters (URL seeding, live URL updates, and default-value cleanup), with shared frontend registration logic.
Changes:
- Extend Checkbox/Toggle and ColorPicker protobufs with an optional
query_param_keyfield used for query-param binding. - Add backend
bindvalidation + wiring to widget protos and registration, including color hex normalization/validation for URL-seeded values. - Add a reusable frontend hook (
useQueryParamBinding) and unit/e2e tests for the new binding behavior.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| proto/streamlit/proto/ColorPicker.proto | Adds query_param_key to ColorPicker proto for URL binding. |
| proto/streamlit/proto/Checkbox.proto | Adds query_param_key to Checkbox proto (used by checkbox + toggle). |
| lib/tests/streamlit/runtime/state/widgets_test.py | Excludes bind from element-id computation inputs. |
| lib/tests/streamlit/elements/color_picker_test.py | Adds unit tests for bind behavior and invalid bind values. |
| lib/tests/streamlit/elements/checkbox_test.py | Adds parameterized unit tests for checkbox/toggle bind behavior. |
| lib/streamlit/runtime/state/widgets.py | Validates bind and enforces key requirement for query-param binding. |
| lib/streamlit/errors.py | Introduces StreamlitInvalidBindValueError. |
| lib/streamlit/elements/widgets/color_picker.py | Adds bind param, sets proto query_param_key, and normalizes/validates URL-seeded hex colors. |
| lib/streamlit/elements/widgets/checkbox.py | Adds bind param and sets proto query_param_key for checkbox/toggle. |
| frontend/lib/src/hooks/useQueryParamBinding.ts | New hook to register/unregister query param bindings with the WidgetStateManager. |
| frontend/lib/src/hooks/useQueryParamBinding.test.ts | Unit tests for the new hook behavior. |
| frontend/lib/src/components/widgets/ColorPicker/ColorPicker.tsx | Registers query param binding for ColorPicker and adds safer default fallbacks. |
| frontend/lib/src/components/widgets/ColorPicker/ColorPicker.test.tsx | Adds unit tests verifying binding registration/unregistration for ColorPicker. |
| frontend/lib/src/components/widgets/Checkbox/Checkbox.tsx | Registers query param binding for checkbox/toggle. |
| frontend/lib/src/components/widgets/Checkbox/Checkbox.test.tsx | Adds unit tests verifying binding registration/unregistration for Checkbox/Toggle. |
| e2e_playwright/st_toggle_test.py | Adds e2e coverage for toggle URL seeding/sync/default cleanup/invalid URL handling. |
| e2e_playwright/st_toggle.py | Adds bound toggle examples to the toggle e2e app. |
| e2e_playwright/st_color_picker_test.py | Adds e2e coverage for color picker URL seeding/sync/default cleanup/invalid URL handling (incl. 3-char hex). |
| e2e_playwright/st_color_picker.py | Adds bound color picker examples to the color picker e2e app. |
| e2e_playwright/st_checkbox_test.py | Adds e2e coverage for checkbox URL seeding/sync/default cleanup/invalid URL handling. |
| e2e_playwright/st_checkbox.py | Adds bound checkbox examples to the checkbox e2e app. |
Comments suppressed due to low confidence (1)
lib/streamlit/runtime/state/widgets.py:138
- For
bind='query-params', an empty-stringkey(e.g.key="") will currently pass validation becauseuser_keyis""(notNone). That results inquery_param_keybeing set to an empty string, which will produce a malformed URL param name and the frontend hook will treat it as “not bound”. Consider rejecting empty-string keys here (e.g. treatuser_key == ""as invalid) and raising aStreamlitAPIExceptionwith a clear message.
# Validate that widget with bind="query-params" has a provided key
if bind == "query-params":
user_key = user_key_from_element_id(element_id)
if user_key is None:
raise StreamlitAPIException(
"Widget must have a 'key' parameter when using bind='query-params'. "
"The key will be used as the query parameter name."
)
58e746c to
00f8c92
Compare
c89191b to
b215f09
Compare
|
@cusor review |
b215f09 to
30a7d88
Compare
00f8c92 to
e5b021f
Compare
30a7d88 to
143b8da
Compare
SummaryThis PR (Part 3 of a series) adds a
Code QualityPython Backend (Good) The implementation follows Streamlit patterns well:
# Compiled regex for validating hex colors (#RGB or #RRGGBB format)
_HEX_COLOR_RE = re.compile(r"^#(?:[0-9a-fA-F]{3}){1,2}$")Frontend TypeScript (Good)
useEffect(() => {
// Treat null and undefined the same - no binding
if (!queryParamKey) {
return
}
widgetMgr.registerQueryParamBinding(
widgetId,
queryParamKey,
valueType,
defaultValue,
options?.urlFormat,
options?.optionStrings
)
return () => {
widgetMgr.unregisterQueryParamBinding(widgetId)
}
}, [
widgetMgr,
widgetId,
queryParamKey,
valueType,
defaultValue,
options?.urlFormat,
options?.optionStrings,
])Protobuf Changes (Good)
Test CoveragePython Unit Tests (Excellent) Coverage is comprehensive with parameterized tests for checkbox/toggle:
The tests use TypeScript Unit Tests (Excellent) The
Component tests (
E2E Tests (Excellent) Comprehensive coverage for all three widgets (checkbox, toggle, color_picker):
E2E tests follow best practices:
Backwards CompatibilityFully Backwards Compatible
Security & RiskLow Risk
Minor Consideration: The hex color normalization in RecommendationsNo blocking issues found. The following are minor observations:
VerdictAPPROVED: This PR is well-implemented with comprehensive test coverage across Python unit tests, TypeScript unit tests, and E2E tests. The code follows established patterns, is fully backwards compatible, and includes proper error handling. The reusable This is an automated AI review using |
ab5519d to
db991f2
Compare
143b8da to
a8b51d7
Compare
db991f2 to
f72245e
Compare
a66054d to
26f521f
Compare
26f521f to
379fd86
Compare
📈 Significant bundle size change detected
Please verify that this change is expected. |
35fb7f3 to
217bacb
Compare
217bacb to
8e65866
Compare
deb0a57 to
89f6cd3
Compare
|
|
||
| it("re-registers when queryParamKey changes", () => { | ||
| const { rerender } = renderHook( | ||
| ({ queryParamKey }) => |
There was a problem hiding this comment.
The TypeScript Development Guide requires explicit return types for functions. The test function parameter queryParamKey in the renderHook callback is missing a type annotation. Add a type annotation like ({ queryParamKey }: { queryParamKey: string | undefined }) =>.
| ({ queryParamKey }) => | |
| ({ queryParamKey }: { queryParamKey: string | undefined }) => |
Spotted by Graphite Agent (based on custom rule: TypeScript Guide)
Is this helpful? React 👍 or 👎 to let us know.
| queryParamBinding | ||
| ? { | ||
| urlFormat: queryParamBinding.urlFormat, | ||
| optionStrings: queryParamBinding.optionStrings, | ||
| } | ||
| : undefined | ||
| ) |
There was a problem hiding this comment.
suggestion: This needs to be memoized by useMemo or else this is going to cause the downstream effect in useQueryParamBinding to run on every render due to this object being recreated each time this code is executed.
| # Normalize first (add # prefix if missing), then validate | ||
| normalized = _normalize_hex_color(ui_value) | ||
| if not _HEX_COLOR_RE.match(normalized): | ||
| raise ValueError(f"Invalid hex color: {ui_value}") |
There was a problem hiding this comment.
question: Are we sure that raising in deserialize is a pattern we want to introduce? I believe all other deserialize functions tend to provide a fallback value instead. I'm not sure what would happen if we were to raise here, so might be worth looking at aligning this with other patterns for deserialize.

Describe your changes
Bind Widgets to Query Params - Part 3
This PR adds the
bindparameter tost.checkbox,st.toggle, andst.color_pickerto enable two-way sync between widget values and URL query parameters.StreamlitInvalidBindValueErrorfor invalid bind valuesuseQueryParamBindinghook - Extracted reusable hook for registering/unregistering query param bindingsTesting Plan