---
components:
  - rx.recharts.ScatterChart
  - rx.recharts.Scatter
title: Scatter Chart
meta_description: "Create scatter plots and bubble charts in Python with Reflex. Build interactive Recharts scatter charts with multiple series, custom axes, sizing, and tooltips — all in pure Python."
---

# Scatter Chart

```python exec
import reflex as rx
```

Scatter charts in Reflex are built on [Recharts](https://recharts.org/), a React charting library, and created in pure Python. A scatter plot (or scatter chart) always has two value axes to show one set of numerical data along a horizontal (value) axis and another set of numerical values along a vertical (value) axis. The chart displays points at the intersection of an x and y numerical value, combining these values into single data points.

## Simple Example

For a scatter chart we must define an `rx.recharts.scatter()` component for each set of values we wish to plot. Each `rx.recharts.scatter()` component has a `data` prop which clearly states which data source we plot. We also must define `rx.recharts.x_axis()` and `rx.recharts.y_axis()` so that the graph knows what data to plot on each axis.

```python demo graphing
data01 = [
    {"x": 100, "y": 200, "z": 200},
    {"x": 120, "y": 100, "z": 260},
    {"x": 170, "y": 300, "z": 400},
    {"x": 170, "y": 250, "z": 280},
    {"x": 150, "y": 400, "z": 500},
    {"x": 110, "y": 280, "z": 200},
]


def scatter_simple():
    return rx.recharts.scatter_chart(
        rx.recharts.scatter(
            data=data01,
            fill="#8884d8",
        ),
        rx.recharts.x_axis(data_key="x", type_="number"),
        rx.recharts.y_axis(data_key="y"),
        width="100%",
        height=300,
    )
```

## Multiple Scatters

We can also add two scatters on one chart by using two `rx.recharts.scatter()` components, and we can define an `rx.recharts.z_axis()` which represents a third column of data and is represented by the size of the dots in the scatter plot.

```python demo graphing
data01 = [
    {"x": 100, "y": 200, "z": 200},
    {"x": 120, "y": 100, "z": 260},
    {"x": 170, "y": 300, "z": 400},
    {"x": 170, "y": 250, "z": 280},
    {"x": 150, "y": 350, "z": 500},
    {"x": 110, "y": 280, "z": 200},
]

data02 = [
    {"x": 200, "y": 260, "z": 240},
    {"x": 240, "y": 290, "z": 220},
    {"x": 190, "y": 290, "z": 250},
    {"x": 198, "y": 250, "z": 210},
    {"x": 180, "y": 280, "z": 260},
    {"x": 210, "y": 220, "z": 230},
]


def scatter_double():
    return rx.recharts.scatter_chart(
        rx.recharts.scatter(data=data01, fill="#8884d8", name="A"),
        rx.recharts.scatter(data=data02, fill="#82ca9d", name="B"),
        rx.recharts.cartesian_grid(stroke_dasharray="3 3"),
        rx.recharts.x_axis(data_key="x", type_="number"),
        rx.recharts.y_axis(data_key="y"),
        rx.recharts.z_axis(data_key="z", range=[60, 400], name="score"),
        rx.recharts.legend(),
        rx.recharts.graphing_tooltip(),
        width="100%",
        height=300,
    )
```

To learn how to use the `x_axis_id` and `y_axis_id` props, check out the Multiple Axis section of the area chart [documentation](/docs/library/graphing/charts/areachart).

## Dynamic Data

Chart data tied to a State var causes the chart to automatically update when the
state changes, providing a nice way to visualize data in response to user
interface elements. View the "Data" tab to see the substate driving this
calculation of iterations in the Collatz Conjecture for a given starting number.
Enter a starting number in the box below the chart to recalculate.

```python demo exec
class ScatterChartState(rx.State):
    data: list[dict[str, int]] = []

    @rx.event
    def compute_collatz(self, form_data: dict) -> int:
        n = int(form_data.get("start") or 1)
        yield rx.set_value("start", "")
        self.data = []
        for ix in range(400):
            self.data.append({"x": ix, "y": n})
            if n == 1:
                break
            if n % 2 == 0:
                n = n // 2
            else:
                n = 3 * n + 1


def scatter_dynamic():
    return rx.vstack(
        rx.recharts.scatter_chart(
            rx.recharts.scatter(
                data=ScatterChartState.data,
                fill="#8884d8",
            ),
            rx.recharts.x_axis(data_key="x", type_="number"),
            rx.recharts.y_axis(data_key="y", type_="number"),
        ),
        rx.form.root(
            rx.input(placeholder="Enter a number", id="start"),
            rx.button("Compute", type="submit"),
            on_submit=ScatterChartState.compute_collatz,
        ),
        width="100%",
        height="15em",
        on_mount=ScatterChartState.compute_collatz({"start": "15"}),
    )
```

## Legend Type and Shape

```python demo exec
class ScatterChartState2(rx.State):
    legend_types: list[str] = [
        "square",
        "circle",
        "cross",
        "diamond",
        "star",
        "triangle",
        "wye",
    ]

    legend_type: str = "circle"

    shapes: list[str] = [
        "square",
        "circle",
        "cross",
        "diamond",
        "star",
        "triangle",
        "wye",
    ]

    shape: str = "circle"

    data01 = [
        {"x": 100, "y": 200, "z": 200},
        {"x": 120, "y": 100, "z": 260},
        {"x": 170, "y": 300, "z": 400},
        {"x": 170, "y": 250, "z": 280},
        {"x": 150, "y": 400, "z": 500},
        {"x": 110, "y": 280, "z": 200},
    ]

    @rx.event
    def set_shape(self, shape: str):
        self.shape = shape

    @rx.event
    def set_legend_type(self, legend_type: str):
        self.legend_type = legend_type


def scatter_shape():
    return rx.vstack(
        rx.recharts.scatter_chart(
            rx.recharts.scatter(
                data=data01,
                fill="#8884d8",
                legend_type=ScatterChartState2.legend_type,
                shape=ScatterChartState2.shape,
            ),
            rx.recharts.x_axis(data_key="x", type_="number"),
            rx.recharts.y_axis(data_key="y"),
            rx.recharts.legend(),
            width="100%",
            height=300,
        ),
        rx.hstack(
            rx.text("Legend Type: "),
            rx.select(
                ScatterChartState2.legend_types,
                value=ScatterChartState2.legend_type,
                on_change=ScatterChartState2.set_legend_type,
            ),
            rx.text("Shape: "),
            rx.select(
                ScatterChartState2.shapes,
                value=ScatterChartState2.shape,
                on_change=ScatterChartState2.set_shape,
            ),
        ),
        width="100%",
    )
```

## Distinguishing Series with Shapes

When plotting several series on one chart, each `rx.recharts.scatter()` can be given its own `shape` in addition to its own `fill`, so the series stay distinguishable even where their points overlap. Available shapes include `"circle"`, `"square"`, `"triangle"`, `"diamond"`, `"star"`, `"cross"`, and `"wye"`.

```python demo graphing
data01 = [
    {"x": 100, "y": 200},
    {"x": 120, "y": 100},
    {"x": 170, "y": 300},
    {"x": 140, "y": 250},
    {"x": 150, "y": 400},
    {"x": 110, "y": 280},
]

data02 = [
    {"x": 200, "y": 260},
    {"x": 240, "y": 290},
    {"x": 190, "y": 290},
    {"x": 198, "y": 250},
    {"x": 180, "y": 280},
    {"x": 210, "y": 220},
]

data03 = [
    {"x": 130, "y": 150},
    {"x": 160, "y": 180},
    {"x": 220, "y": 170},
    {"x": 250, "y": 200},
    {"x": 175, "y": 130},
    {"x": 205, "y": 160},
]


def scatter_shapes():
    return rx.recharts.scatter_chart(
        rx.recharts.scatter(data=data01, fill="#8884d8", name="A"),
        rx.recharts.scatter(data=data02, fill="#82ca9d", name="B", shape="triangle"),
        rx.recharts.scatter(data=data03, fill="#ffc658", name="C", shape="star"),
        rx.recharts.cartesian_grid(stroke_dasharray="3 3"),
        rx.recharts.x_axis(data_key="x", type_="number"),
        rx.recharts.y_axis(data_key="y"),
        rx.recharts.legend(),
        rx.recharts.graphing_tooltip(),
        width="100%",
        height=300,
    )
```

## Bubble Chart

Adding an `rx.recharts.z_axis()` turns a scatter chart into a bubble chart: the `z` value of each point controls the bubble size, mapped to a pixel `range`. This lets you encode a third dimension of data alongside the x and y position.

```python demo graphing
data = [
    {"x": 100, "y": 200, "z": 200},
    {"x": 120, "y": 100, "z": 260},
    {"x": 170, "y": 300, "z": 400},
    {"x": 140, "y": 250, "z": 280},
    {"x": 150, "y": 400, "z": 500},
    {"x": 110, "y": 280, "z": 200},
]


def bubble_chart():
    return rx.recharts.scatter_chart(
        rx.recharts.scatter(
            data=data,
            fill=rx.color("accent", 8),
        ),
        rx.recharts.x_axis(data_key="x", name="x", type_="number"),
        rx.recharts.y_axis(data_key="y", name="y", type_="number"),
        rx.recharts.z_axis(data_key="z", range=[60, 400], name="size"),
        rx.recharts.graphing_tooltip(),
        width="100%",
        height=300,
    )
```

## Related Charts

Explore more chart types you can build with Reflex and Recharts in pure Python:

- [Line Chart](/docs/library/graphing/charts/linechart)
- [Bar Chart](/docs/library/graphing/charts/barchart)
- [Error Bar](/docs/library/graphing/charts/errorbar)
