Skip to content
Back to skills

Plotly

BSecurity

Plotly is an open-source charting library that draws interactive charts (hover, zoom, pan, export) from Python with plotly.py and Plotly Express, or in the browser with plotly.js. Use when a user asks to plot data with Plotly, make a scatter plot, line chart, bar chart, heatmap, choropleth map, 3D chart or subplot grid, export a chart to HTML or PNG, build a Dash dashboard, add a chart to a React app with react-plotly.js, or fix a chart that broke after upgrading to plotly.py 6/7 or plotly.js...

  • 142 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 6, 2026
developmentjavascripttypescriptpythongojavabashreactnextjsexpressaws

Works with

  • terminal
  • api

Security analysis

B88/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill plotly --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Plotly?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Plotly
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-plotly/badge)](https://www.skillsdirectory.com/skills/terminalskills-plotly)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: plotly
description: >-
  Plotly is an open-source charting library that draws interactive charts (hover, zoom, pan, export) from Python with plotly.py and Plotly Express, or in the browser with plotly.js. Use when a user asks to plot data with Plotly, make a scatter plot, line chart, bar chart, heatmap, choropleth map, 3D chart or subplot grid, export a chart to HTML or PNG, build a Dash dashboard, add a chart to a React app with react-plotly.js, or fix a chart that broke after upgrading to plotly.py 6/7 or plotly.js 3/4.
license: Apache-2.0
compatibility: "Python 3.8+ for plotly.py (Dash needs 3.9+). Static image export needs Chrome or Chromium. plotly.js runs in any modern browser."
metadata:
  author: terminal-skills
  version: "1.1.0"
  category: data-ai
  repository: https://github.com/plotly/plotly.py
  tags: ["charts", "interactive", "python", "javascript", "dashboard"]
---
# Plotly — Interactive Scientific Visualization

## Overview

Plotly is an interactive charting library for Python and JavaScript. plotly.py builds a figure (a JSON description of traces and layout) and plotly.js renders it in the browser with hover tooltips, zoom and export; Plotly Express is the one-call API on top, Dash turns figures into web dashboards, and react-plotly.js wraps plotly.js for React. This skill follows plotly.py 7.1 (which bundles plotly.js 4.1), Dash 4.4 and react-plotly.js 4.1.

## Instructions

### Python (Plotly Express)

```python
import plotly.express as px

# Scatter plot with color and size encoding
gap = px.data.gapminder().query("year == 2007")
fig = px.scatter(
    gap, x="gdpPercap", y="lifeExp",
    size="pop", color="continent",
    hover_name="country",
    log_x=True,
    size_max=60,
    title="GDP vs Life Expectancy (2007)"
)
fig.show()

# Time series with multiple lines
stocks = px.data.stocks()
fig = px.line(stocks, x="date", y=["GOOG", "AAPL", "AMZN", "FB", "MSFT"],
              title="Stock Prices Over Time")
fig.update_layout(yaxis_title="Price ($)", legend_title="Company")

# Heatmap of a correlation matrix
corr = px.data.iris().drop(columns=["species", "species_id"]).corr()
fig = px.imshow(corr, text_auto=".2f", color_continuous_scale="RdBu_r",
                title="Feature Correlation Matrix")

# Bar chart of totals per category: px.histogram sums the rows (px.bar draws one bar per row, so aggregate first)
tips = px.data.tips()
fig = px.histogram(tips, x="day", y="total_bill", histfunc="sum", color="sex", barmode="group",
                   category_orders={"day": ["Thur", "Fri", "Sat", "Sun"]})

# Geographic choropleth
fig = px.choropleth(
    gap, locations="iso_alpha", color="gdpPercap",
    hover_name="country",
    color_continuous_scale="Viridis",
    title="GDP Per Capita by Country"
)
```

Plotly Express accepts pandas, Polars and PyArrow data frames directly (since 6.0, through Narwhals), as well as dicts and lists.

Tile maps use `px.scatter_map`, `px.line_map`, `px.density_map` and `px.choropleth_map` (MapLibre, no access token). The `*_mapbox` functions and `go.Scattermapbox` were removed in 7.0.

### Subplots and graph objects

Non-cartesian traces (indicator, pie, 3D, maps) need a matching entry in `specs`; without it `add_trace` raises `ValueError: Trace type 'indicator' is not compatible with subplot type 'xy'`.

```python
from plotly.subplots import make_subplots
import plotly.graph_objects as go

months = ["2026-01", "2026-02", "2026-03", "2026-04", "2026-05", "2026-06"]
revenue = [41200, 43850, 47310, 45120, 52480, 58930]
users = [1830, 1912, 2054, 2101, 2260, 2415]
churn = [2.9, 2.7, 2.8, 2.4, 2.2, 2.1]

fig = make_subplots(
    rows=2, cols=2,
    specs=[[{"type": "xy"}, {"type": "xy"}], [{"type": "xy"}, {"type": "indicator"}]],
    subplot_titles=("Revenue", "Users", "Churn %", ""),
)
fig.add_trace(go.Scatter(x=months, y=revenue, mode="lines+markers"), row=1, col=1)
fig.add_trace(go.Scatter(x=months, y=users, mode="lines"), row=1, col=2)
fig.add_trace(go.Scatter(x=months, y=churn, fill="tozeroy"), row=2, col=1)
fig.add_trace(go.Indicator(mode="gauge+number", value=72, title={"text": "NPS"},
                           gauge={"axis": {"range": [0, 100]}}), row=2, col=2)
fig.update_layout(height=600, showlegend=False)
```

### Export

```python
config = {"showSendToCloud": False, "displaylogo": False}

fig.show(config=config)                                  # browser or notebook
fig.write_html("report.html", include_plotlyjs="cdn", config=config)   # ~10 KB + CDN script
fig.write_html("offline.html", config=config)            # self-contained, embeds ~4.8 MB of plotly.js
fig.write_image("chart.png", width=1000, height=500, scale=2)          # also .svg, .pdf, .jpeg, .webp
```

`write_image` needs Kaleido 1.x and a Chrome or Chromium install. If none is found, run `plotly_get_chrome` (or `plotly.io.get_chrome()`). The `engine=` argument and Orca support were removed in 7.0. `plotly.io.write_images([...], [...])` exports many figures faster than a loop.

### JavaScript (Plotly.js)

Since plotly.js 3, titles must be objects: `title: "Monthly Revenue"` is ignored without an error.

```typescript
import Plotly from "plotly.js-dist-min";

const months = ["2026-01-01", "2026-02-01", "2026-03-01", "2026-04-01", "2026-05-01", "2026-06-01"];
const revenue = [41200, 43850, 47310, 45120, 52480, 58930];

// Renders into the element with id="revenue-chart"
Plotly.newPlot("revenue-chart", [
  {
    x: months,
    y: revenue,
    type: "scatter",
    mode: "lines+markers",
    name: "Revenue",
    line: { color: "#4f46e5", width: 2 },
    hovertemplate: "%{x|%b %Y}<br>$%{y:,.0f}<extra></extra>",
  },
], {
  title: { text: "Monthly Revenue" },
  xaxis: { title: { text: "Month" } },
  yaxis: { title: { text: "Revenue ($)" }, tickformat: "$,.0f" },
  hovermode: "x unified",
}, { responsive: true, displaylogo: false, showSendToCloud: false });

// Update in place: pass the complete new traces and layout — anything left out (axis titles, colors) reverts to defaults
Plotly.react("revenue-chart", [{ x: months, y: revenue.map((v) => v * 1.1) }], { title: { text: "Forecast" } });
```

### React (react-plotly.js)

The default export needs the full `plotly.js` package as a peer dependency. To use a prebuilt or partial bundle instead, build the component with the factory (`npm install react-plotly.js plotly.js-basic-dist-min`; the partial bundles ship no type declarations, so TypeScript also needs `npm install -D @types/plotly.js-basic-dist-min`):

```tsx
import Plotly from "plotly.js-basic-dist-min";   // partial bundle: scatter, bar and pie only, ~1.2 MB
import createPlotlyComponent from "react-plotly.js/factory";

const Plot = createPlotlyComponent(Plotly);

export function RevenueChart({ months, revenue }: { months: string[]; revenue: number[] }) {
  return (
    <Plot
      data={[{ x: months, y: revenue, type: "scatter", mode: "lines+markers" }]}
      layout={{ autosize: true, title: { text: "Monthly Revenue" } }}
      config={{ displaylogo: false, showSendToCloud: false }}
      style={{ width: "100%", height: "360px" }}
      useResizeHandler
    />
  );
}
```

The plot redraws only when `data`, `layout` or `config` change identity, so create new arrays and objects instead of mutating them. plotly.js needs a browser: importing it during server-side rendering throws `ReferenceError: self is not defined`, so in Next.js load this component with `next/dynamic` and `ssr: false`.

### Dash (Python Web Framework)

```python
# app.py — interactive dashboard with Plotly + Dash
from dash import Dash, html, dcc, callback, Output, Input
import plotly.express as px

df = px.data.gapminder()
continents = sorted(df["continent"].unique())

app = Dash(__name__)

app.layout = html.Div([
    html.H1("Life expectancy by continent"),
    dcc.Dropdown(id="continent", options=continents, value="Europe", clearable=False),
    dcc.Graph(id="life-exp", config={"showSendToCloud": False}),
])

@callback(Output("life-exp", "figure"), Input("continent", "value"))
def update_chart(continent):
    subset = df[df["continent"] == continent]
    return px.line(subset, x="year", y="lifeExp", color="country",
                   title=f"Life expectancy in {continent}")

if __name__ == "__main__":
    app.run(debug=True)   # http://127.0.0.1:8050 — app.run_server() was removed in Dash 3
```

## Installation

```bash
pip install "plotly[express]" pandas      # plotly.py + numpy for Plotly Express
pip install kaleido                        # static image export (needs Chrome: plotly_get_chrome)
pip install dash                           # Dash framework
npm install plotly.js-dist-min             # JavaScript, full minified bundle
npm install react-plotly.js plotly.js      # React wrapper + its peer dependency
```

## Examples

### Example 1: Chart for a report, as HTML and PNG

**User request:** "Plot monthly revenue from `revenue.csv` and give me an interactive HTML file plus a PNG for the slide deck."

```python
# revenue_chart.py — columns in revenue.csv: month (YYYY-MM-DD), revenue
import pandas as pd
import plotly.express as px

sales = pd.read_csv("revenue.csv", parse_dates=["month"])

fig = px.line(sales, x="month", y="revenue", markers=True, title="Monthly revenue")
fig.update_traces(hovertemplate="%{x|%b %Y}<br>$%{y:,.0f}<extra></extra>")
fig.update_layout(hovermode="x unified", yaxis_tickprefix="$", yaxis_tickformat=",.0f")

fig.write_html("revenue.html", include_plotlyjs="cdn", config={"showSendToCloud": False})
fig.write_image("revenue.png", width=1000, height=500, scale=2)
```

```bash
pip install "plotly[express]" pandas kaleido
python revenue_chart.py
```

Result: `revenue.html` (about 8 KB; it loads plotly.js from `cdn.plot.ly`, so it needs network access when opened) and `revenue.png` at 2000×1000 px. On a machine without Chrome the `write_image` line fails with "Kaleido requires Google Chrome to be installed"; `plotly_get_chrome -y` fixes that.

### Example 2: Upgrade a plotly.js 2 chart that lost its titles

**User request:** "After updating plotly.js our chart has no title or axis labels and there is a new Share button in the toolbar."

```diff
 Plotly.newPlot("signups-chart", traces, {
-  title: "Weekly signups",
-  xaxis: { title: "Week", titlefont: { size: 12 } },
-  yaxis: { title: "Signups" },
-});
+  title: { text: "Weekly signups" },
+  xaxis: { title: { text: "Week", font: { size: 12 } } },
+  yaxis: { title: { text: "Signups" } },
+}, { showSendToCloud: false });
```

Check any figure before shipping with `Plotly.validate`, which lists every key the current version rejects:

```javascript
console.log(Plotly.validate(traces, layout));
// [{ code: "object", msg: "In layout, key title must be linked to an object container", ... }]
```

After the change the title and both axis labels render again and the Share button is gone from the modebar. If the chart used `scattermapbox` traces, rename them to `scattermap` and the `mapbox` layout key to `map` in the same pass.

## Guidelines

1. **Plotly Express first** — `px.scatter`, `px.line`, `px.bar`, `px.histogram` and friends cover most charts; drop to `go.Figure` and `add_trace` for mixed trace types or fine control. Every `px` call returns a normal `go.Figure` you can keep editing with `update_layout` and `update_traces`.
2. **Hover templates** — `%{x}`, `%{y}`, `%{customdata[0]}` with d3 formats (`%{y:,.0f}`, `%{x|%b %Y}`); end with `<extra></extra>` to hide the trace-name box.
3. **The Share button uploads data** — plotly.js 4 shows a "Share chart" modebar button by default that sends the figure to Plotly Cloud after a confirmation. Set `showSendToCloud: False` in `config` for internal or sensitive data; this applies to `fig.show`, `write_html`, `dcc.Graph(config=...)` and `Plotly.newPlot`.
4. **Static export depends on Chrome** — Kaleido 1.x no longer ships a browser. In containers and CI, install Chromium or run `plotly_get_chrome -y` in the image build, not at request time.
5. **Bundle size** — the full `plotly.js-dist-min` is about 4.8 MB minified (1.5 MB gzipped). When you only need a few trace types use a partial bundle: `plotly.js-basic-dist-min` (scatter, bar, pie; ~1.2 MB), `-cartesian-`, `-geo-`, `-gl3d-`, `-finance-` or `-map-`.
6. **Colors in plotly.js 4** — `rgb(0.2, 0.4, 0.6)` is no longer read as fractions of 255 (it now renders almost black) and `hsv()` strings are rejected; use 0–255 values, hex or `hsl()`.
7. **Large data** — SVG rendering slows down past a few thousand points. Plotly Express picks WebGL on its own (`render_mode="auto"`); with graph objects or plotly.js use `go.Scattergl` / `type: "scattergl"`, or aggregate before plotting.
8. **Notebooks** — plotly.py 6+ needs Jupyter Notebook 7+ or JupyterLab; `go.FigureWidget` also needs `pip install anywidget`.
9. **`app.run(debug=True)` is for development** — the Dash debug mode enables a code-reloading dev server; deploy with a WSGI server such as `gunicorn app:server` after exposing `server = app.server`.
10. **3D sparingly** — 3D charts are hard to read; use 2D unless the third dimension adds real insight.

Files in this skill

  • SKILL.md5.6 KB
  • _scores.json1.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…