Skip to main content

Flash - async port of the Dash framework

Project description

logo

Flash is an async‑first, drop‑in replacement for Dash. It swaps Flask for Quart (ASGI) under the hood so your Dash apps can run true async callbacks, speak Server‑Sent Events (SSE), and use websockets without workarounds. On top of the Dash API you know, Flash adds streaming superpowers designed to work together:

  • event_callback — fire on server-side events and long‑running tasks
  • stream_props (used inside event_callback) — yield progressive UI updates directly into component props via native SSE

Build reactive dashboards that don’t just update—they stream.

Table of contents

Why use Flash?

  • Async everywhere: Write async def callbacks and endpoints; await databases, APIs, and background work naturally.
  • Progressive rendering: Push partial results as they’re ready using event_callback + stream_props (native SSE, no polling).
  • Real‑time UX: Out‑of‑the‑box SSE and websockets for live metrics, logs, and notifications.
  • Familiar by design: Keep Dash’s component model, layout patterns, and callback signatures—just add async and streaming when you need it.

How is it different from Dash?

  • Runtime: Quart (ASGI) instead of Flask (WSGI) to enable native async I/O.
  • Callbacks: Supports async def callbacks and async endpoints without threads or hacks.
  • Streaming: New event_callback decorator and stream_props helper provide native SSE streams for progressive prop updates
  • Realtime transports: SSE and websockets are first‑class citizens.
  • Compatibility: Most Dash apps run as‑is. For streaming use cases, switch to event_callback or add stream_props to target props; deploy with an ASGI server.

Getting started

Install

pip install dash-flash

Basic app

from flash import Flash, Input, Output, callback, html

app = Flash(__name__)

btn = html.Button("click me", id="button")
content = html.Div(id="output")

app.layout = html.Div([btn, content])

@callback(
    Output(content, "children"),
    Input(btn, "n_clicks")
)
async def update(clicked):
    return "Hello World"

Basic Callbacks

Async callbacks don’t inherently speed up your application, but they enable efficient concurrency for I/O-bound workloads. If a callback performs heavy or blocking work, keep it synchronous—such tasks are executed in a dedicated thread. A typical use case is aggregating responses from multiple HTTP endpoints or running parallel database queries.

from .data import get_data_1, get_data_2, get_data_3
from .figures import create_figures

from flash import Input, Output, callback
import asyncio

@callback(
    Ouput("figure-container", "children"),
    Input("input", "value"),
)
async def update(value):
    data = await asyncio.gather(
        get_data_1(value),
        get_data_2(value),
        get_data_3(value)
    )

    updated_figure = create_figures(data)
    return updated_figures

Event Callback

Server-Sent Events (SSEs) are a server push technology that keeps an HTTP connection open, allowing servers to continuously stream updates to clients. They are typically used for sending messages, data streams, or real-time updates directly to the browser via the native JavaScript EventSource API.

fvent callbacks build on this principle by using async generator functions that yield updates instead of returning once. This enables:

  • Progressive UI updates (e.g., streaming partial results).
  • Endless streams (e.g., real-time dashboards, stock tickers, monitoring).

The API mirrors Dash’s callback design, but with two key differences:

  1. No explicit output needed – updates are applied with stream_props.
  2. stream_props behaves like set_props, needs to be yield.

Stream Props

The stream_props function allows you to send UI updates on the fly and follows the set_props API by Dash, while enhancing it with batch updates which reduces network overhead and quicker UI updates. The function can be used as follows:

# Single updates
yield stream_props(component_id="cid", props={"children": "Hello Stream"})
yield stream_props("cid", {"children": "Hello Stream"})
# Batch updates
yield stream_props(batch=[
    ("cid", {"children": "Hello Stream"}),
    ("btn", {"disablesd": True}),
])
yield stream_props([
    ("cid", {"children": "Hello Stream"}),
    ("btn", {"disablesd": True}),
])

Basic Event Callback

This example (from Dash’s background callback docs) shows how a background callback is no longer necessary—eliminating the need for extra services like Celery + Redis.

# data.py
import pandas as pd
import asyncio

async def get_data(chunk_size: int):
    df: pd.DataFrame = data.gapminder()
    total_rows = df.shape[0]

    while total_rows > 0:
        await asyncio.sleep(2)
        end = len(df) - total_rows + chunk_size
        total_rows -= chunk_size
        update_data = df[:end].to_dict("records")
        df.drop(df.index[:end], inplace=True)
        yield update_data, df.columns

A more realistic use case would be streaming query results with SQLAlchemy async:

# data.py
from sqlalchemy.ext.asyncio import AsyncConnection

async def get_data(connection: AsyncConnection):
    result = await connection.stream(select(users_table))

    async for partition in result.partitions(100):
        print("list of rows: %s" % partition)
        return partition

Hooking it into your app with event_callback:

#app.py
from flash import Input, event_callback, stream_props

@event_callback(Input("start-stream-button", "n_clicks"))
async def update_table(_):

    yield stream_props([
        ("start-stream-button", {"loading": True}),
        ("cancel-stream-button", {"display": "flex"})
    ])

    progress = 0
    chunk_size = 500
    async for data_chunk, colnames in get_data(chunk_size):
        if progress == 0:
            columnDefs = [{"field": col} for col in colnames]
            update = {"rowData": data_chunk, "columnDefs": columnDefs}
        else:
            update = {"rowTransaction": {"add": data_chunk}}

        yield stream_props("dash-ag-grid", update)

        if len(data_chunk) == chunk_size:
            yield NotificationsContainer.send_notification(
                title="Starting stream!",
                message="Notifications in Dash, Awesome!",
                color="lime",
            )

        progress += 1

    yield stream_props("start-stream-button", {"loading": False, "children": "Reload"})
    yield stream_props("reset-strea-button", {"display": "none"})

Endless Event Callback

Event callbacks are lightweight and stateless, making them ideal for continuous real-time streams:

from .data import get_latest_stock_data
from flash import Input, event_callback, stream_props

@callback(Input("start-stream-button", "n_clicks"))
async def stream_stock_data(_):
    while True:
        x, s1, s2 = await get_latest_stock_data()
        update = [dict(x=[[x], [x]], y=[[s1], [s2]]), [0, 1], 100]

    yield stream_props("stock-graph", {"extendData": update})

Cancel streams

  • Configure cancel=[(Input("id", "value"), desired_state), ...] on @event_callback to close the active SSE stream when a condition is NOT met. Lets consider a layout with three tabs and the stream runs in tab dashboard and you want that the stream only runs when that tab is open, you can set (Input("tabs", "value"), "dashboard")
  • Proper canceling prevents stale EventSource connections from pushing late updates, reduces network chatter, and avoids extra rendering work in the browser.
  • On cancel, marks the SSE as done and applies any reset_props so the UI returns to a clean, predictable state.
  • Common triggers: reset/cancel buttons, page or tab changes, or beginning a new run that invalidates the previous stream.
@event_callback(
    ...,
    cancel=[
        (Input("tabs", "value"), "dashboard"),
        (Input("c-btn", "n_clicks"), 0)
    ]
)

Handle error

  • on_error is a callable that receives the raised error (e.g., def on_error(e): ...). Use it to emit a final user-facing message and perform cleanup when the stream fails.
  • Typical cleanup: unsubscribe from a stream topic, close file/DB handles, revoke a background task token, or write telemetry.
  • Flash also emits an SSE error signal so the client can coordinate default error UI and cleanup. Combine with reset_props to leave the interface in a known-good state.

Reset props

  • Configure reset_props=[(component_id, {prop: value, ...}), ...] on @event_callback to restore the UI after cancel or error.
  • Use it to re-enable start buttons, hide cancel controls, clear progress text/spinners, and restore placeholders.
  • These updates are applied automatically when a stream is canceled or errors, alongside closing the SSE connection and clearing transient state.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dash_flash-1.3.1.tar.gz (75.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dash_flash-1.3.1-py3-none-any.whl (82.7 kB view details)

Uploaded Python 3

File details

Details for the file dash_flash-1.3.1.tar.gz.

File metadata

  • Download URL: dash_flash-1.3.1.tar.gz
  • Upload date:
  • Size: 75.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.2 CPython/3.12.7 Darwin/24.6.0

File hashes

Hashes for dash_flash-1.3.1.tar.gz
Algorithm Hash digest
SHA256 61cbfb1f678222f6097383f076a8bc407fd519b861f5d296f4f3f100174bd345
MD5 4e0a5fcb021db818ea1b7ebc45e1fe9b
BLAKE2b-256 5c4e6defea1476783ee739ff3bbf19d042b90d4b194a0d635b6e4ae3ee0a9245

See more details on using hashes here.

File details

Details for the file dash_flash-1.3.1-py3-none-any.whl.

File metadata

  • Download URL: dash_flash-1.3.1-py3-none-any.whl
  • Upload date:
  • Size: 82.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.2 CPython/3.12.7 Darwin/24.6.0

File hashes

Hashes for dash_flash-1.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a2da0f90aa46631842399f33fa5596c6d94add57cb978d01a91f650ceffd07e9
MD5 a7eb3762e17e3a69aa4994bc12a4f2e7
BLAKE2b-256 e18c55a0b5ebb16731b88216357ea4fde88e5e78fb9e74da7b8028c6a03ecb76

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page