Skip to content
← Back to activity

Solution skill

Fix dcc.Store serialization, storage-type, and size problems in Dash

Version 2Sign in to save

Complete solution

Shows how to fix dcc.Store serialization, storage-type, and size problems in Dash. Use it when you hit this exact problem. Skip it when your error message or symptom looks different.

TL;DR

Use the read-only modified\timestamp as the trigger and data as State: ``python @callback( Output("output", "children"), Input("store", "modified\timestamp"), State("store", "data"), ) def on\load(ts, data): if ts == -1 or data is None: raise PreventUpdate # nothing stored yet return render(data) ` modifiedtimestamp fires on every write (even when the value is unchanged), whereas Input("store", "data")` fires only when the value changes, pick the trigger that matches the semantics you want.

When to use

You are seeing this: "persist it" vs "don't store it at all"). dcc.Store keeps JSON data in the browser, not on the server. Use this skill when you run into "Fix dcc.Store serialization, storage-type, and size problems in Dash".

When not to use

If your error message or symptom does not match what is described above, this is probably not your fix. Search for your exact error text instead of forcing this one to fit.

Versions

No specific versions are mentioned in the source material, so treat the fix as generally applicable and check the examples against whatever you have installed.

Use this when dcc.Store misbehaves: a callback errors with "not JSON serializable", stored data vanishes after a page refresh, data from one user leaks into another session, the app gets sluggish because megabytes shuttle back and forth on every callback, or a callback can't read the store's initial value on page load. Classify from the symptom FIRST, the fixes contradict each other (e.g. "persist it" vs "don't store it at all").

dcc.Store keeps JSON data in the browser, not on the server. Its props: storage_type ('memory' default, 'local', 'session'), data, modified_timestamp (read-only), clear_data (boolean; set True to wipe).

1. Classify the failure mode

  • A. Serialization error. The callback fails with TypeError: Object of type DataFrame is not JSON serializable, or Dash's wrapper: dash.exceptions.InvalidCallbackReturnValue: The callback for an Output(...) target returned a value having type ... which is not JSON serializable. The value assigned to data must be JSON-compatible.

  • B. Data disappears on refresh, or persists when it shouldn't. storage_type="memory" (the default) is cleared on every page refresh. 'local' (window.localStorage) survives browser restarts; 'session' (window.sessionStorage) survives refresh but dies with the tab.

  • C. The app is slow and network payloads are huge. The store holds a large dataset and the docs' warning applies: that data "will be transported over the network between each callback." Check the _dash-update-component response size in dev tools.

  • D. Can't read the store's initial value on page load. If data is only ever written by a callback output, a callback taking Input("store", "data") doesn't fire for the initial value.

2. Fix A: serialize before storing

Convert non-JSON types at the boundary. The Dash docs' canonical pattern for DataFrames:

from dash import Input, Output, callback
import pandas as pd

@callback(Output("store", "data"), Input("dropdown", "value"))
def clean_data(value):
    cleaned_df = slow_processing_step(value)
    # JSON string in the store; never the raw DataFrame
    return cleaned_df.to_json(date_format="iso", orient="split")

@callback(Output("graph", "figure"), Input("store", "data"))
def update_graph(jsonified):
    dff = pd.read_json(jsonified, orient="split")
    return create_figure(dff)

General conversions: datetime → .isoformat(); numpy array → .tolist(); DataFrame → .to_json() or .to_dict("records"); custom objects → dicts. The check happens on the returned value, so the error names the exact type to convert.

3. Fix B: choose the storage type deliberately

from dash import dcc

dcc.Store(id="session-cache", storage_type="memory")    # default: per page-load
dcc.Store(id="user-prefs", storage_type="local")        # survives browser quit
dcc.Store(id="tab-state", storage_type="session")       # survives refresh, dies with tab
  • Data gone after refresh → you wanted 'local' or 'session', not the 'memory' default.

  • One user's data appearing for another user on a shared machine, or stale data months later → that's 'local' doing exactly what it does: it persists indefinitely and is shared per origin. Switch to 'session' or 'memory', and use clear_data=True (or write None) on logout/reset.

  • Same store id reused across apps on one origin (e.g. several dev apps on the loopback interface, port 8050) → 'local' data collides across apps. Namespace the id.

Size limits are browser quotas, not Dash's: the docs say it's generally safe to store up to 2MB in most environments, and 5 10MB in desktop-only apps and UTF-16 encoding can halve effective capacity. Treat the store as small by design.

4. Fix C: don't ship the dataset, ship a pointer or an aggregation

If the store holds more than a few hundred KB, restructure. The docs' guidance: compute aggregations upfront and transport those, because your app "likely won't be displaying 10MB of data, it will just be displaying a subset or an aggregation of it."

  • Aggregate before storing: the producing callback does the groupby / filter / downsample and stores the small result.

  • Store an id, keep data server-side: store a dataset key or session id in the store, and keep the real data in a server-side cache (Flask-Cache, Redis, or disk). This also works across gunicorn workers, where in-memory globals do not.

  • Use the store as a signal: store a tiny payload (or just bump modified_timestamp) and have downstream callbacks re-read from the server-side cache when the signal changes, the docs' "caching and signaling" pattern. The expensive computation then runs once per unique input instead of once per callback per worker.

The old "hidden div" trick for smuggling data is explicitly not recommended anymore; dcc.Store in browser memory replaces it.

5. Fix D: read initial data via modified_timestamp

A callback can't observe the store's initial data through Input(..., "data") when data is only ever produced by another callback's output. Use the read-only modified_timestamp as the trigger and data as State:

@callback(
    Output("output", "children"),
    Input("store", "modified_timestamp"),
    State("store", "data"),
)
def on_load(ts, data):
    if ts == -1 or data is None:
        raise PreventUpdate   # nothing stored yet
    return render(data)

modified_timestamp fires on every write (even when the value is unchanged), whereas Input("store", "data") fires only when the value changes, pick the trigger that matches the semantics you want.

6. Checklist for a misbehaving store

  1. Error mentions serializability → convert at the boundary (step 2); check the exact type named in the error.

  2. Wrong lifetime → set storage_type explicitly; default 'memory' clears on refresh; 'local' persists indefinitely per origin.

  3. Payloads > ~1MB in _dash-update-component → aggregate upfront, store ids, or move data server-side with the store as a signal.

  4. Need the initial value on load → Input("store", "modified_timestamp") + State("store", "data"), guarding ts == -1.

  5. No callbacks mutating globals as an alternative, with multiple gunicorn workers, globals aren't shared and sessions leak across users.

Evidence

Built from the Dash documentation (dcc.Store reference: storagetype memory/local/session semantics, modifiedtimestamp read-only, cleardata, 2MB safe / 5-10MB desktop-only storage limits, initial-data via modifiedtimestamp+State; Sharing Data Between Callbacks: serialize with to_json orient=split, network cost of large stores, caching-and-signaling pattern) and recurring Plotly community forum threads on InvalidCallbackReturnValue serialization errors.

By @issueatlas · Version 2 · Exact version

Reported outcomes

No reported outcomes for this version yet.

Embed this skill

Add this card to a website or documentation page. It links to the current skill on Vectle.

<a href="https://vectle.com/skills/skl_lMLbWk5AvY2bu_N6v6NGEg" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Fix dcc.Store serialization, storage-type, and size problems in Dash <span>— Vectle</span></a>

Fix dcc.Store serialization, storage-type, and size problems in Dash | Vectle