Back to Reflex

Bar Chart

docs/library/graphing/charts/barchart.md

0.9.8a114.7 KB
Original Source

Bar Chart

python
import reflex as rx
import random

Bar charts in Reflex are built on Recharts, a React charting library, and let you visualize categorical data in pure Python. A bar chart presents categorical data with rectangular bars whose heights or lengths are proportional to the values that they represent.

For a bar chart we must define an rx.recharts.bar() component for each set of values we wish to plot. Each rx.recharts.bar() component has a data_key which clearly states which variable in our data we are tracking. In this simple example we plot uv as a bar against the name column which we set as the data_key in rx.recharts.x_axis.

Simple Example

python
data = [
    {"name": "Page A", "uv": 4000, "pv": 2400, "amt": 2400},
    {"name": "Page B", "uv": 3000, "pv": 1398, "amt": 2210},
    {"name": "Page C", "uv": 2000, "pv": 9800, "amt": 2290},
    {"name": "Page D", "uv": 2780, "pv": 3908, "amt": 2000},
    {"name": "Page E", "uv": 1890, "pv": 4800, "amt": 2181},
    {"name": "Page F", "uv": 2390, "pv": 3800, "amt": 2500},
    {"name": "Page G", "uv": 3490, "pv": 4300, "amt": 2100},
]


def bar_simple():
    return rx.recharts.bar_chart(
        rx.recharts.bar(
            data_key="uv",
            stroke=rx.color("accent", 9),
            fill=rx.color("accent", 8),
        ),
        rx.recharts.x_axis(data_key="name"),
        rx.recharts.y_axis(),
        data=data,
        width="100%",
        height=250,
    )

Multiple Bars

Multiple bars can be placed on the same bar_chart, using multiple rx.recharts.bar() components. Drawn side by side like this, they form a grouped (or clustered) bar chart.

python
data = [
    {"name": "Page A", "uv": 4000, "pv": 2400, "amt": 2400},
    {"name": "Page B", "uv": 3000, "pv": 1398, "amt": 2210},
    {"name": "Page C", "uv": 2000, "pv": 9800, "amt": 2290},
    {"name": "Page D", "uv": 2780, "pv": 3908, "amt": 2000},
    {"name": "Page E", "uv": 1890, "pv": 4800, "amt": 2181},
    {"name": "Page F", "uv": 2390, "pv": 3800, "amt": 2500},
    {"name": "Page G", "uv": 3490, "pv": 4300, "amt": 2100},
]


def bar_double():
    return rx.recharts.bar_chart(
        rx.recharts.bar(
            data_key="uv",
            stroke=rx.color("accent", 9),
            fill=rx.color("accent", 8),
        ),
        rx.recharts.bar(
            data_key="pv",
            stroke=rx.color("green", 9),
            fill=rx.color("green", 8),
        ),
        rx.recharts.x_axis(data_key="name"),
        rx.recharts.y_axis(),
        data=data,
        width="100%",
        height=250,
    )

Stacked Bar Chart

To build a stacked bar chart, give each rx.recharts.bar() the same stack_id. Instead of being drawn side by side, the bars are stacked on top of one another, which is ideal for showing part-to-whole composition (also called a segmented bar chart). Set stack_offset="expand" on the bar_chart to turn it into a 100% stacked bar chart.

python
data = [
    {"name": "Page A", "uv": 4000, "pv": 2400, "amt": 2400},
    {"name": "Page B", "uv": 3000, "pv": 1398, "amt": 2210},
    {"name": "Page C", "uv": 2000, "pv": 9800, "amt": 2290},
    {"name": "Page D", "uv": 2780, "pv": 3908, "amt": 2000},
    {"name": "Page E", "uv": 1890, "pv": 4800, "amt": 2181},
    {"name": "Page F", "uv": 2390, "pv": 3800, "amt": 2500},
    {"name": "Page G", "uv": 3490, "pv": 4300, "amt": 2100},
]


def bar_stacked():
    return rx.recharts.bar_chart(
        rx.recharts.bar(
            data_key="uv",
            stack_id="1",
            fill=rx.color("accent", 8),
        ),
        rx.recharts.bar(
            data_key="pv",
            stack_id="1",
            fill=rx.color("green", 8),
        ),
        rx.recharts.x_axis(data_key="name"),
        rx.recharts.y_axis(),
        rx.recharts.legend(),
        data=data,
        width="100%",
        height=300,
    )

Ranged Charts

You can also assign a range in the bar by assigning the data_key in the rx.recharts.bar to a list with two elements, i.e. here a range of two temperatures for each date.

python
range_data = [
    {"day": "05-01", "temperature": [-1, 10]},
    {"day": "05-02", "temperature": [2, 15]},
    {"day": "05-03", "temperature": [3, 12]},
    {"day": "05-04", "temperature": [4, 12]},
    {"day": "05-05", "temperature": [12, 16]},
    {"day": "05-06", "temperature": [5, 16]},
    {"day": "05-07", "temperature": [3, 12]},
    {"day": "05-08", "temperature": [0, 8]},
    {"day": "05-09", "temperature": [-3, 5]},
]


def bar_range():
    return rx.recharts.bar_chart(
        rx.recharts.bar(
            data_key="temperature",
            stroke=rx.color("accent", 9),
            fill=rx.color("accent", 8),
        ),
        rx.recharts.x_axis(data_key="day"),
        rx.recharts.y_axis(),
        data=range_data,
        width="100%",
        height=250,
    )

Stateful Charts

Here is an example of a bar graph with a State. Here we have defined a function randomize_data, which randomly changes the data for both graphs when the first defined bar is clicked on using on_click=BarState.randomize_data.

python
class BarState(rx.State):
    data = data

    @rx.event
    def randomize_data(self):
        for i in range(len(self.data)):
            self.data[i]["uv"] = random.randint(0, 10000)
            self.data[i]["pv"] = random.randint(0, 10000)
            self.data[i]["amt"] = random.randint(0, 10000)


def bar_with_state():
    return rx.recharts.bar_chart(
        rx.recharts.cartesian_grid(
            stroke_dasharray="3 3",
        ),
        rx.recharts.bar(
            data_key="uv",
            stroke=rx.color("accent", 9),
            fill=rx.color("accent", 8),
        ),
        rx.recharts.bar(
            data_key="pv",
            stroke=rx.color("green", 9),
            fill=rx.color("green", 8),
        ),
        rx.recharts.x_axis(data_key="name"),
        rx.recharts.y_axis(),
        rx.recharts.legend(),
        on_click=BarState.randomize_data,
        data=BarState.data,
        width="100%",
        height=300,
    )

Click Events and Drill-Down

The on_click event on rx.recharts.bar provides no arguments, so it cannot tell you which bar was clicked. To handle clicks with data — for example, to drill down into a category — render an rx.recharts.cell for each data point with rx.foreach, so each cell binds its click data at render time from the loop variable.

In this example, clicking a bar filters the chart to that category (zoom in), and clicking the same bar again clears the filter (zoom out). An rx.cond on the cell's fill highlights the selected bar.

python
drilldown_data = [
    {"name": "Fiction", "count": 42},
    {"name": "History", "count": 28},
    {"name": "Science", "count": 35},
    {"name": "Biography", "count": 17},
    {"name": "Fantasy", "count": 24},
]

DRILLDOWN_COLORS = ["#6366F1", "#8B5CF6", "#A855F7", "#D946EF", "#EC4899"]


class BarDrilldownState(rx.State):
    drilled_genre: str = ""

    @rx.var
    def genre_data(self) -> list[dict[str, str | int]]:
        if self.drilled_genre:
            return [d for d in drilldown_data if d["name"] == self.drilled_genre]
        return drilldown_data

    @rx.event
    def toggle_genre(self, genre_name: str):
        """Click the same bar again to zoom back out."""
        if self.drilled_genre == genre_name:
            self.drilled_genre = ""
        else:
            self.drilled_genre = genre_name


def _drilldown_cell(item: rx.Var, index: rx.Var) -> rx.Component:
    """Each bar gets its own cell with on_click bound to the item name."""
    return rx.recharts.cell(
        on_click=BarDrilldownState.toggle_genre(item["name"].to(str)),
        custom_attrs={"cursor": "pointer"},
        fill=rx.cond(
            BarDrilldownState.drilled_genre == item["name"],
            "#1D4ED8",
            rx.Var.create(DRILLDOWN_COLORS)[index % len(DRILLDOWN_COLORS)],
        ),
    )


def bar_drilldown():
    return rx.recharts.bar_chart(
        rx.recharts.cartesian_grid(stroke_dasharray="3 3", vertical=False),
        rx.recharts.x_axis(data_key="name", tick_line=False, axis_line=False),
        rx.recharts.y_axis(allow_decimals=False, axis_line=False, tick_line=False),
        rx.recharts.bar(
            rx.foreach(BarDrilldownState.genre_data, _drilldown_cell),
            data_key="count",
            radius=[4, 4, 0, 0],
        ),
        rx.recharts.graphing_tooltip(),
        data=BarDrilldownState.genre_data,
        width="100%",
        height=300,
    )

The key points of the pattern:

  1. Define a helper function that takes (item, index) from rx.foreach and returns an rx.recharts.cell.
  2. Bind the click with on_click=State.handler(item["name"]) — the handler receives a plain value because it is bound at render time, not extracted from the click event.
  3. Use rx.cond to highlight the selected item with a different fill.
  4. Apply the drill filter to the chart's own data, so only the clicked item remains visible; clearing the filter restores all items.

The same pattern works for pie charts: place the rx.foreach of cells inside rx.recharts.pie.

Example with Props

Here's an example demonstrates how to customize the appearance and layout of bars using the bar_category_gap, bar_gap, bar_size, and max_bar_size props. These props accept values in pixels to control the spacing and size of the bars.

python
data = [
    {"name": "Page A", "value": 2400},
    {"name": "Page B", "value": 1398},
    {"name": "Page C", "value": 9800},
    {"name": "Page D", "value": 3908},
    {"name": "Page E", "value": 4800},
    {"name": "Page F", "value": 3800},
]


def bar_features():
    return rx.recharts.bar_chart(
        rx.recharts.bar(
            data_key="value",
            fill=rx.color("accent", 8),
        ),
        rx.recharts.x_axis(data_key="name"),
        rx.recharts.y_axis(),
        data=data,
        bar_category_gap="15%",
        bar_gap=6,
        bar_size=100,
        max_bar_size=40,
        width="100%",
        height=300,
    )

Rounded Bars

The radius prop on rx.recharts.bar rounds the corners of each bar. Pass a single number to round all four corners, or a list of four values in the order [top-left, top-right, bottom-right, bottom-left] — for example [8, 8, 0, 0] rounds only the top corners.

python
data = [
    {"name": "Page A", "uv": 4000, "pv": 2400, "amt": 2400},
    {"name": "Page B", "uv": 3000, "pv": 1398, "amt": 2210},
    {"name": "Page C", "uv": 2000, "pv": 9800, "amt": 2290},
    {"name": "Page D", "uv": 2780, "pv": 3908, "amt": 2000},
    {"name": "Page E", "uv": 1890, "pv": 4800, "amt": 2181},
    {"name": "Page F", "uv": 2390, "pv": 3800, "amt": 2500},
    {"name": "Page G", "uv": 3490, "pv": 4300, "amt": 2100},
]


def bar_rounded():
    return rx.recharts.bar_chart(
        rx.recharts.bar(
            data_key="uv",
            fill=rx.color("accent", 8),
            radius=[8, 8, 0, 0],
        ),
        rx.recharts.x_axis(data_key="name"),
        rx.recharts.y_axis(),
        data=data,
        width="100%",
        height=250,
    )

Gradient Fill

Bars can be styled with SVG linear gradients. Define one gradient per series inside an rx.el.svg.defs block as the first child of the chart, then reference each gradient from the bar's fill prop with "url(#gradient-id)".

python
data = [
    {"name": "Page A", "uv": 4000, "pv": 2400, "amt": 2400},
    {"name": "Page B", "uv": 3000, "pv": 1398, "amt": 2210},
    {"name": "Page C", "uv": 2000, "pv": 9800, "amt": 2290},
    {"name": "Page D", "uv": 2780, "pv": 3908, "amt": 2000},
    {"name": "Page E", "uv": 1890, "pv": 4800, "amt": 2181},
    {"name": "Page F", "uv": 2390, "pv": 3800, "amt": 2500},
    {"name": "Page G", "uv": 3490, "pv": 4300, "amt": 2100},
]


def create_bar_gradient(color: str, gradient_id: str) -> rx.Component:
    return rx.el.svg.linear_gradient(
        rx.el.svg.stop(offset="5%", stop_color=color, stop_opacity=0.8),
        rx.el.svg.stop(offset="95%", stop_color=color, stop_opacity=0.2),
        id=gradient_id,
        x1=0,
        y1=0,
        x2=0,
        y2=1,
    )


def bar_gradient():
    return rx.recharts.bar_chart(
        rx.el.svg.defs(
            create_bar_gradient("#8884d8", "bar_gradient_uv"),
            create_bar_gradient("#82ca9d", "bar_gradient_pv"),
        ),
        rx.recharts.bar(
            data_key="uv",
            fill="url(#bar_gradient_uv)",
            radius=[4, 4, 0, 0],
        ),
        rx.recharts.bar(
            data_key="pv",
            fill="url(#bar_gradient_pv)",
            radius=[4, 4, 0, 0],
        ),
        rx.recharts.x_axis(data_key="name"),
        rx.recharts.y_axis(),
        rx.recharts.graphing_tooltip(),
        rx.recharts.legend(),
        data=data,
        width="100%",
        height=300,
    )

Vertical Example

The layout prop allows you to set the orientation of the graph to be vertical or horizontal, it is set horizontally by default. Setting layout="vertical" makes the bars run left-to-right, which is how you create a horizontal bar chart in Reflex.

md
# Include margins around your graph to ensure proper spacing and enhance readability. By default, provide margins on all sides of the chart to create a visually appealing and functional representation of your data.
python
data = [
    {"name": "Page A", "uv": 4000, "pv": 2400, "amt": 2400},
    {"name": "Page B", "uv": 3000, "pv": 1398, "amt": 2210},
    {"name": "Page C", "uv": 2000, "pv": 9800, "amt": 2290},
    {"name": "Page D", "uv": 2780, "pv": 3908, "amt": 2000},
    {"name": "Page E", "uv": 1890, "pv": 4800, "amt": 2181},
    {"name": "Page F", "uv": 2390, "pv": 3800, "amt": 2500},
    {"name": "Page G", "uv": 3490, "pv": 4300, "amt": 2100},
]


def bar_vertical():
    return rx.recharts.bar_chart(
        rx.recharts.bar(
            data_key="uv",
            stroke=rx.color("accent", 8),
            fill=rx.color("accent", 3),
        ),
        rx.recharts.x_axis(type_="number"),
        rx.recharts.y_axis(data_key="name", type_="category"),
        data=data,
        layout="vertical",
        margin={"top": 20, "right": 20, "left": 20, "bottom": 20},
        width="100%",
        height=300,
    )

To learn how to use the sync_id, stack_id,x_axis_id and y_axis_id props check out the of the area chart documentation, where these props are all described with examples.

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