dkedar7
fast_dash
Python

Turn your Python functions into interactive apps! Fast Dash is an innovative way to deploy your Python code as interactive web apps with minimal changes.

Last updated Aug 6, 2026
129
Stars
19
Forks
8
Issues
0
Stars/day
Attention Score
82
Language breakdown
Python 96.7%
CSS 2.3%
JavaScript 1.0%
Files click to expand
README

Fast Dash

Turn any Python function into a web app with a single decorator ⚡

Release Status CI Status MIT License Documentation Downloads Coverage


  • Documentation: docs.fastdash.app
  • Source: github.com/dkedar7/fast_dash
  • Install: pip install fast-dash
  • Claude Code users: /plugin marketplace add dkedar7/fast_dash then /plugin install fast-dash@fast-dash to load the Fast Dash skill — agents will pick it up automatically when you ask them to "turn this function into a web app".

What is Fast Dash?

Fast Dash inspects your Python function's signature, picks UI components from the type hints and default values, and serves the result as a Plotly Dash app — usually in under five lines of code. No frontend, no callbacks, no boilerplate.

It exists for one job: collapse the gap between a working Python function and a shareable interactive web app.

Fast Dash demo

30-second example

pip install fast-dash
from fast_dash import fastdash

@fastdash def greet(name: str = "world") -> str: return f"Hello, {name}!"

Serving on http://127.0.0.1:8080

That's the entire app. Open the URL, type a name, click Run, see the response.

Chat apps

Pass chat=True and your callback becomes a streaming chat app — a composer, a scrolling transcript, and per-session history — with no LLM provider baked in:

from fast_dash import fastdash

@fastdash(chat=True) def assistant(query: str): for token in my_llm.stream(query): # any provider — you choose yield token

yield strings to stream the reply as markdown; add a history parameter for multi-turn memory, and any other parameter becomes a sidebar setting (tucked into a collapsible accordion when there are many). chat= also accepts a LangGraph graph, a (query, ctx) callable, or a chat-model instance.

Or keep a normal app and add an assistant beside it — pass your agent as chat=: the agent reads your app's live inputs (ctx.inputs) and can drive it (setinput / runapp) — anything a user can do, the agent can do.

from fast_dash import FastDash

def dashboard(revenue: int = 100, region: str = "West") -> str: return f"{region}: ${revenue}"

def assistant(query, ctx): # ctx.inputs holds the app's live values yield {"type": "set_input", "name": "revenue", "value": 250} yield {"type": "run_app"} yield "Bumped revenue to 250 and re-ran."

FastDash(callbackfn=dashboard, chat=assistant, chattitle="Helper").run()

Give the agent tools

Pass a chat model (or chat=True with chat_model=) and Fast Dash auto-builds a LangChain assistant wired to your app. Its tools — read the app, set inputs, run it, set individual outputs, rearrange the layout, run Python — come from agenttoolkit(app), trimmed to the chattools allowlist you choose:

from fast_dash import FastDash

def dashboard(revenue: int = 100, region: str = "West") -> str: return f"{region}: ${revenue}"

The default toolkit lets the agent drive inputs, set outputs, re-mosaic the

layout, and run Python (with approval). Narrow it with chat_tools=.

FastDash( callback_fn=dashboard, chat=True, chatmodel="openai:gpt-4o-mini", # or a model instance / FASTDASHMODEL chattools=("readapp", "setinput", "runapp"), # read-only + drive, no code exec ).run()

Install the extra with pip install "fast-dash[agent]". See the chat guide.

How it works

The @fastdash decorator does three things at import time:

  • Inspects the function signature — each parameter becomes an input component, the return becomes an output component.
  • Picks components from type hints and defaultsint → number input, bool → checkbox, pd.DataFrame → table, etc. (full table below).
  • Builds a Dash app and starts the server — the function body is wired as the callback that runs when the user clicks Run.
You can override any of this by passing components explicitly via inputs= and outputs=, or by skipping the decorator and using the FastDash(...) class directly.

Type hint → component reference

Inputs (parameter type → UI component):

| Type hint | Component | | --- | --- | | str | Single-line text input | | str with a multi-line / long default | Text area | | str with a hex-color default (e.g. "#1c7ed6") | Color picker | | str with default=[...] | Single-select dropdown | | int, float | Number input | | int/float with range(...) default | Slider | | bool | Checkbox | | list | Multi-select dropdown | | dict (with default) | Multi-select dropdown (keys) | | datetime.date | Date picker | | PIL.Image.Image | Image upload | | Literal["a", "b"] | Single-select dropdown | | enum.Enum subclass | Single-select dropdown | | Annotated[int, range(0, 100)] | Slider | | Annotated[str, ["a", "b"]] | Single-select dropdown | | Optional[T] | As T, but nullable | | Any Dash component instance | Used directly | | Any Fast Dash component (Text, Slider, ...) | Used directly |

Outputs (return type → UI component):

| Return type | Component | | --- | --- | | str, int, float, etc. | Text (rendered as <h1>) | | pd.DataFrame | Table | | PIL.Image.Image, matplotlib.figure.Figure | Image | | plotly.graph_objects.Figure (or string-form "go.Figure", "Figure") | Plotly chart | | Tuple of types | Multiple outputs (one component each) | | Any Fast Dash component (Graph, Image, ...) | Used directly |

from fast_dash import fastdash, Graph

@fastdash def chart(rows: int = 100) -> Graph: import plotly.express as px return px.scatter(px.data.iris().head(rows), x="sepalwidth", y="sepallength")

Unknown hints fall back to text. Source-introspection failures (REPL, exec) fall back to generic OUTPUT1, OUTPUT2 labels — the app still works.

Built-in components

The package exports ready-to-use Fast Dash components you can pass directly as inputs= or outputs=:

| Component | Use as | Notes | | --- | --- | --- | | Text, TextArea, PasswordInput | input or output | Single-line, multi-line, masked text | | NumberInput, Slider | input | Numeric with optional bounds | | Switch | input | Toggle (True / False) | | MultiSelect | input | Multi-select dropdown | | DateInput, DateRange | input | Single date / date range picker | | ColorInput | input | Color picker, returns hex | | Upload, UploadImage | input | File / image upload | | Graph, Image, Table, Markdown | output | Plotly chart, image, DataFrame table, rendered Markdown | | Chat | output | Streaming chat history (with stream=True) | | Download | output | Triggers a browser download |

Common patterns

Multiple inputs and outputs

from fast_dash import fastdash

@fastdash def describe(text: str, count: int = 3) -> str: """Repeat text count times.""" return " · ".join([text] * count)

Mosaic layout for arranging multiple outputs:

from fast_dash import fastdash, Graph
import plotly.express as px
import pandas as pd

@fastdash(mosaic="AB\nAC") def dashboard(rows: int = 100) -> (Graph, Graph, Graph): df = px.data.iris().head(rows) return ( px.scatter(df, x="sepalwidth", y="sepallength", color="species"), px.histogram(df, x="petal_width"), px.box(df, y="petal_length", color="species"), )

The mosaic string is ASCII art (inspired by Matplotlib's subplot_mosaic). Each letter corresponds to one output, in order.

Wrapping arbitrary Dash components with Fastify:

from fast_dash import fastdash, Fastify
from dash import dcc

custom_slider = Fastify(dcc.Slider(min=0, max=100, value=50), "value")

@fastdash def myapp(x: customslider) -> str: return f"You picked {x}"

Cascading inputs with depends_on — wire one input's options to another input's value:

from fastdash import fastdash, dependson

countries = { "USA": ["California", "Texas", "New York"], "India": ["Maharashtra", "Karnataka", "Delhi"], }

@fastdash def pick_state( country: str = list(countries), state: str = depends_on("country", lambda c: countries[c]), ) -> str: return f"{state}, {country}"

The resolver receives the parent input's current value. Return:

  • a list to set the dependent dropdown's options (and clear its value),
  • a dict like {"data": [...], "value": ...} to set both, or
  • a scalar to set just the value.
Skip the decorator when you want more control over the lifecycle:

from fast_dash import FastDash

def my_fn(x: int) -> int: return x * 2

app = FastDash(callbackfn=myfn, title="Doubler", port=8050) app.run()

Multiple functions in a single tabbed app — pass a list of callbacks:

from fast_dash import FastDash

def greet(name: str) -> str: return f"Hello, {name}!"

def add(a: int, b: int) -> int: return a + b

app = FastDash([greet, add], tab_titles=["Greeter", "Adder"]) app.run()

Each function gets its own tab with independent inputs, outputs, and callbacks. tab_titles is optional — without it, tabs are named after the functions.

Multi-step pipelines with steps= — chain functions into a wizard, threading outputs forward via from_step:

from fastdash import FastDash, fromstep
import pandas as pd

def load_data(rows: int = 100) -> pd.DataFrame: """Load a sample dataset.""" return pd.DataFrame({"x": range(rows), "y": [i * 2 for i in range(rows)]})

def double(data=fromstep(loaddata)) -> pd.DataFrame: """Double every value.""" return data * 2

def summarise(data=from_step(double), prefix: str = "Result:") -> str: """One-line summary.""" return f"{prefix} {len(data)} rows, sum={data.values.sum()}"

FastDash(steps=[load_data, double, summarise], title="Pipeline Demo").run()

Each step is shown one at a time with a stepper progress indicator. Click Run to execute the active step, then Next to advance. Use fromstep(prevfn) as a parameter default to wire an upstream output into a downstream input. Steps without from_step parameters can mix in regular UI inputs (like prefix above).

Drive your app from an AI agent (MCP)

Pass mcpserver=True and your app serves a web UI and an MCP server — built on Dash's native MCP (Dash ≥ 4.3) and mounted on the same port — so any MCP-capable agent (Claude Code, Cursor, Cline, …) can inspect and drive it.

from fast_dash import fastdash
import plotly.graph_objects as go

@fastdash(mcp_server=True) # web UI AND MCP on :8080/mcp def plot_bars(n: int = 6, color: str = "#1c7ed6") -> go.Figure: ...

Point an agent at http://localhost:8080/mcp:

{"servers": {"my-app": {"url": "http://localhost:8080/mcp"}}}

The agent calls describeapp() to discover the input contract (each input's id, type, default, options, and current value), then drives the app with setinput / setinputs / invoke / setform / getinvocation / listcomponenttypes. Dash's native dash://layout / dash://components / getdash_component expose the static component tree. Agent mutations apply to the live browser within ~500 ms (no reload).

# From the agent's side, in one call:
invoke(inputs={"n": 12, "color": "#2f9e44"})   # set inputs and run, one round-trip

Agent-generated UI with DynamicDash — the form materializes when an agent calls the set_form tool:

from fast_dash import DynamicDash, Graph, Markdown

app = DynamicDash( callback_fn=score, placeholder="Ask the agent to call set_form() to build the form.", output_components=[Graph, Markdown], mcp_server=True, ) app.run(port=8052) # run() mounts the MCP server on :8052/mcp

Real-time push (opt-in). On the default Flask backend, agent mutations reach the browser via a ~500 ms polling drain. Install fast-dash[fastapi] and pass backend="fastapi" to switch to Dash's ASGI backend, where updates stream over a WebSocket via set_props (sub-100 ms, no polling):

@fastdash(mcp_server=True, backend="fastapi")   # real-time WebSocket push
def plot_bars(n: int = 6) -> go.Figure:
    ...

Notes: the MCP route shares the web app's host/port and has no authentication — keep it loopback in development. Multi-function and steps modes skip the MCP surface.

Decorator options

Most apps need none of these — defaults are sensible. Pass any of them as kwargs to @fastdash(...) or FastDash(...).

| Option | Default | Effect | | --- | --- | --- | | title | function name | App title shown in the header | | inputs, outputs | inferred | Override component selection | | mosaic | None | ASCII layout for multiple outputs | | theme | "JOURNAL" | Any Bootswatch theme name | | port | 8080 | Port to serve on | | mode | None | Set to "jupyterlab", "inline", or "external" for notebook use | | update_live | False | Re-run on every input change instead of waiting for the Run button | | about | True | Show the docstring as an "About" modal; pass a string to override | | minimal | False | Hide chrome (header, footer, nav) for embedding | | branding | False | Show the Fast Dash rocket footer | | stream | False | Enable streaming outputs (see docs) | | mcp_server | False | Also serve an MCP server (Dash-native, on the web app's port at /mcp) so AI agents can drive the app (see above) | | backend | None | "fastapi" (needs fast-dash[fastapi]) for the ASGI backend + real-time WebSocket push; default is Flask |

The full list lives in the docs.

Limits and gotchas

  • Output labels are inferred from the return line of your source. If the source can't be retrieved (REPL, exec, frozen environments), Fast Dash falls back to generic OUTPUT1, OUTPUT2 labels rather than crashing. Pass output_labels=[...] explicitly to control them.
  • Reusing component instances across inputs and outputs can mutate shared attributes. Construct fresh components per slot (or use inputs=Text rather than inputs=text_instance).
  • The theme arg expects a Bootswatch name, not a CSS URL. It sets light/dark mode (dark for CYBORG, DARKLY, QUARTZ, SLATE, SOLAR, SUPERHERO, VAPOR); Bootswatch CSS still loads and styles dbc-rendered bits (e.g. data tables). To set the accent colour of the Mantine chrome (buttons, links, focus rings, chat bubbles), pass accent="indigo" — a Mantine colour name — rather than expecting theme to carry it.

Development

git clone https://github.com/dkedar7/fast_dash.git
cd fast_dash
uv pip install -e ".[test]"   # or: pip install -e ".[test]"
uv pip install "dash[testing]"
uv run --no-sync pytest tests/

Selenium tests need a chromedriver matching your installed Chrome version (brew install --cask chromedriver on macOS).

The tests/ directory is the canonical example collection — tests/examples.py and tests/testtypinghints.py cover most patterns the library supports.

Project structure

fast_dash/
  fast_dash.py     # FastDash class + @fastdash decorator + callback wiring
  Components.py    # Layout (AppLayout) + type-hint inference + built-in components
  utils.py         # Source introspection, theme mapping, docstring parsing
  assets/          # Static CSS served by Dash
tests/             # pytest suite (also serves as runnable examples)
docs/              # MkDocs site source

License

MIT. Built on top of Plotly Dash.

🔗 More in this category

© 2026 GitRepoTrend · dkedar7/fast_dash · Updated daily from GitHub