Skip to content

Improvement: reduce component serialization payload by excluding defaults and None values#577

Draft
krzysztof-causalens wants to merge 6 commits intomasterfrom
improvement/remove-default-values
Draft

Improvement: reduce component serialization payload by excluding defaults and None values#577
krzysztof-causalens wants to merge 6 commits intomasterfrom
improvement/remove-default-values

Conversation

@krzysztof-causalens
Copy link
Copy Markdown
Collaborator

@krzysztof-causalens krzysztof-causalens commented Feb 7, 2026

Motivation and Context

Component serialization included all field values — even defaults like bold: false, italic: false, underline: false, background: null, direction: 'vertical', searchable: false, etc. — on every single component instance. For a typical page with dozens of Stacks, Buttons, Tables, and other components this wastes significant bandwidth, since the client already knows the defaults.

Before:

Screenshot 2026-02-07 at 16 51 14

After:
Screenshot 2026-02-07 at 16 44 57

22% decrease on the demo components page, depends on the components used. In a different local app I've seen almost a 50% decrease (125KB -> 75KB)

Implementation Description

1. _exclude_when_default mechanism (definitions.py)

Added a _exclude_when_default ClassVar on ComponentInstance and StyledComponentInstance that lists fields to omit from serialization when they match their declared default. The serializer resolves defaults per-class via type(self).model_fields, so subclass overrides (e.g. Stack.hug=False vs StyledComponentInstance.hug=None) are handled correctly.

  • ComponentInstance excludes: raw_css, track_progress, error_handler, fallback, id_, for_
  • StyledComponentInstance extends with ~25 styling fields (bold, italic, underline, align, background, hug, all dimensions, etc.)
  • position is intentionally kept — LayoutComponent overrides it to 'relative' and clients depend on receiving it

2. Opt-in None exclusion via _exclude_none_when_default flag (definitions.py)

Added a _exclude_none_when_default: ClassVar[bool] flag on ComponentInstance (default False). When True, the serializer strips any field where the value is None and the field's declared default is None. This catches every Optional[X] = None field automatically without needing to list them in _exclude_when_default.

This is opt-in, not global, to avoid breaking downstream components that may use strict === null checks on props. First-party components opt in via:

  • BaseDashboardComponent (the base for all dara-components) sets the flag to True
  • For (extends ComponentInstance directly) sets the flag to True

Downstream components extending ComponentInstance or StyledComponentInstance directly keep the default False (backwards compatible).

Fields where None is not the default (e.g. track_progress: bool | None = False) are NOT stripped when explicitly set to None.

3. Component-specific _exclude_when_default extensions

Extended _exclude_when_default on commonly used components to also exclude their component-specific fields at defaults:

Component Excluded fields
Stack collapsed, direction, scroll (keeps hug — its False default differs from parent's None)
Button outline, stop_click_propagation
Text formatted
Select multiselect, searchable, max_rows
Table multi_select, searchable, include_index, show_checkboxes, suppress_click_events_for_selection

4. Client-side fixes

For fields where undefined (missing prop) doesn't behave identically to the Python default, added client-side fallbacks via destructuring defaults:

  • Stack: { direction = 'vertical', ...props }DisplayCtx.Provider and styled component need an explicit value
  • Select: { max_rows = 3, ...props } — direct passthrough to UI component
  • Table: { include_index = true, show_checkboxes = true, ...props } — truthy/falsy checks invert for True-default booleans
  • Card: !== null!= null for title/subtitle — only component with strict null checks on props
  • For: === null== null for virtualization — prevented fallthrough to virtualized render path
  • Text: bold/italic/underline ternaries now emit undefined instead of 'normal'/'none'
  • useComponentStyles: hug-inheritance check changed from !== false to == null; Text.align default changed from 'left' to None

5. Other

  • Text.align default changed from 'left' to None — CSS text-align defaults to start anyway

Any new dependencies Introduced

None.

How Has This Been Tested?

  • Updated unit tests
  • Spot checked in a demo app locally

PR Checklist:

  • I have implemented all requirements? (see JIRA, project documentation).
  • I am not affecting someone else's work, If I am, they are included as a reviewer.
  • I have added relevant tests (unit, integration or regression).
  • I have added comments to all the bits that are hard to follow.
  • I have added/updated Documentation.
  • I have updated the appropriate changelog with a line for my changes.

Reduce payload size by not serializing styling fields when they match their
class defaults. Uses _exclude_when_default ClassVar on ComponentInstance and
StyledComponentInstance, resolved via model_fields for correct per-class
defaults. Client-side fixes for bold/italic/underline, hug inheritance,
and Stack hug defaulting.
…on-side override, simplify Text align default
@krzysztof-causalens krzysztof-causalens changed the title Improvement: exclude default styling fields from component serialization Improvement: reduce component serialization payload by excluding defaults and None values Feb 7, 2026
@krzysztof-causalens krzysztof-causalens self-assigned this Feb 7, 2026
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