Build data apps with the Python tools you know.

Panel is an open-source library for interactive dashboards and web apps. Widgets, plots and layouts are ordinary Python objects that you connect to your data and models, pass between functions and classes, and test like the rest of your code. There are no string IDs to match up and no script that reruns on every click.

pip install panel
Window30 days
Volatility1.0
Series
observed30 day mean

Import your libraries.

import numpy as np, panel as pnimport holoviews as hv pn.extension()

Create the controls.

window = pn.ui.IntSlider(name="Window", value=30, start=5, end=90)sigma  = pn.ui.FloatSlider(name="Volatility", value=1.0, start=0.2, end=3)ticker = pn.ui.RadioButtonGroup(name="Series", options=["AAPL", "MSFT", "NVDA"])

Define the plot.

def series(window, sigma, ticker):    rng = np.random.default_rng(sum(map(ord, ticker)))    walk = np.cumsum(rng.normal(0.05, sigma, 400))    smooth = np.convolve(walk, np.ones(window) / window, "same")    return hv.Curve(walk).opts(alpha=0.4) * hv.Curve(smooth)

Connect the controls and arrange the app.

plot = pn.bind(series, window, sigma, ticker)pn.ui.Row(pn.ui.Column(window, sigma, ticker), plot).servable()

Update only what needs to change

When a user changes a control, Panel runs only the code connected to it. In a small app, rerunning everything is cheap and the difference is hard to notice. In a real application that loads large datasets, fits models and keeps user state, it determines how quickly the app responds and how much caching and state management you have to write to keep it that way.

Full-script execution

alpha = 0.1 → 0.5
load 4M rowsruns again
clean and joinruns again
fit the modelruns again
draw the forecastruns again
draw the data tableruns again

In this example, a full script rerun repeats all five steps unless caching or other controls are added.

Panel

alpha = 0.1 → 0.5
load 4M rowsunchanged
clean and joinunchanged
fit the modelruns again
draw the forecastruns again
draw the data tableunchanged

Only the model and forecast update. The dataset and table stay in place.

Compare Panel and Streamlit

AI tools for developers and users

Use HoloViz skills to help coding assistants work with Panel, and Lumen to add an assistant to your app.

Build with coding assistants

HoloViz skills give coding assistants guidance on building, reviewing, debugging and testing Panel apps with current APIs. Install them for Claude Code, Codex, Copilot, Cursor, Gemini CLI and other supported tools.

pip install holoviz-skillsholoviz-skills install
Browse the skills

Let users explore through chat

Lumen lets users change filters and adjust views through plain-language requests. The assistant works with the app's existing controls and respects their available options and limits.

from lumen.ai import Plannerfrom lumen.ai.agents import ChatAgent, ComponentControlAgentfrom lumen.ai.llm import OpenAI agent = ComponentControlAgent(components=page)assistant = Planner(agents=[ChatAgent, agent], llm=OpenAI())drawer = pn.ui.Drawer(assistant, anchor="right", variant="docked")page.main.append(drawer)
Run the penguin copilot
A chat assistant beside a penguin dashboard, showing updated filters and chart settings
One request updates the species filter, x-axis and color setting in Lumen's penguin copilot.

Organize your app as it grows

Connect widgets to functions for a simple app, or use reusable components to organize a larger one. Both approaches work together.

Connect a function

Use pn.bind to connect a Python function to widgets. Panel updates the result when their values change.

plot = pn.bind(view, df, window=window) pn.ui.Column(window, plot).servable()

Create a reusable component

Group data, settings and behavior in a component. Param lets you define valid values and how changes affect the app, making components easier to reuse and test.

class Explorer(pn.viewable.Viewer):    data = param.DataFrame(allow_refs=True)    window = param.Integer(30, bounds=(5, 90))     @param.depends("data", "window")    def plot(self):        return view(self.data, self.window)

Combine components

Build pages from smaller components that share data and stay in sync.

class Dashboard(pn.viewable.Viewer):    data = param.DataFrame()     def __panel__(self):        ref = self.param.data        return pn.ui.Tabs(            ("Explore", Explorer(data=ref)),            ("Report", Report(data=ref)),        )

Built this way, Panel apps run in production as multi-page internal platforms with authentication, editable tables, review workflows and chat assistants used by whole teams. Lumen, a complete AI data exploration application, is built entirely with Panel. Because Panel keeps a live Python session for every visitor, it suits apps built around data, models and computation. A consumer product such as a public message board or a full word processor is better served by a general-purpose web framework, which can still embed Panel apps through FastAPI or Django.

Choosing between functions and classes

From development to deployment

Work in a notebook or your preferred editor, then deploy the same app for others to use.

1

In a notebook

Explore data with live widgets and plots in Jupyter, JupyterLab, VS Code, Colab or marimo.

pn.ui.Row(controls, plot)
2

From your editor

Build your app in a Python file, mark the layout with .servable() and run panel serve. Development mode reloads the app as you edit.

panel serve app.py --dev
3

In production

Host your app on your own infrastructure, add authentication, or integrate it with FastAPI or Django. Compatible apps can also run entirely in the browser with WebAssembly.

Browser deployment

panel convert app.py

Developed in the open, since 2018

Panel is maintained by a team at Anaconda together with contributors from research labs, banks, and instrument makers. Ask questions on Discourse or Discord, and report bugs on GitHub.

GitHub stars
5.8k
First release
2018
Contributors
220+
License
BSD-3