R

ReflexKR

Reflex Docs

The Component Workshop: Reflex's 100 Library Components

·

Course overview

Layout

·

Box

·

Flex

·

Stack

·

Grid

·

Container

·

Section

·

Center

·

Spacer

·

Separator

·

Card

·

Inset

·

Aspect Ratio

·

Fragment

Typography

·

Text

·

Heading

·

Link

·

Code

·

Markdown

·

Blockquote

·

Quote

·

Strong

·

Em

·

Kbd

Forms

·

Button

·

Input

·

Text Area

·

Checkbox

·

Switch

·

Radio Group

·

Select

·

Slider

·

Form

·

Upload

Data display

·

Badge

·

Avatar

·

Icon

·

Callout

·

List

·

Data List

·

Code Block

·

Progress

·

Spinner

·

Scroll Area

·

Moment

Disclosure

·

Tabs

·

Accordion

·

Segmented Control

Overlay

·

Tooltip

·

Popover

·

Hover Card

·

Dialog

·

Alert Dialog

·

Drawer

·

Dropdown Menu

·

Context Menu

·

Toast

Dynamic rendering

·

Cond

·

Match

·

Foreach

·

Auto Scroll

Media

·

Image

·

Audio

·

Video

Tables and data grids

·

Table

·

Data Table

·

Data Editor

Charts

·

Area Chart

·

Bar Chart

·

Line Chart

·

Composed Chart

·

Pie Chart

·

Radar Chart

·

Radial Bar Chart

·

Scatter Chart

·

Funnel Chart

·

Treemap

·

Sankey Chart

·

Error Bar

Chart parts

·

Axis

·

Cartesian Grid

·

Chart Tooltip

·

Legend

·

Label

·

Reference

·

Brush

Other chart libraries

·

Plotly

·

Pyplot

HTML elements

·

HTML

·

HTML Text

·

HTML Layout

·

HTML Forms

·

HTML Media

·

HTML Tables

·

SVG

Other tools

·

Theme

·

Skeleton

·

Clipboard

·

Html Embed

·

Script

·

Memo

The Component Workshop: Reflex's 100 Library Components
Charts

Pie Chart

rx.recharts.pie_chartrx.recharts.pierx.recharts.cell

Official docs

Workshop order

Please show last month's visitors by browser as a donut, with the name and value visible for each slice.

rx.recharts.pie_chart treats the whole as 100 and divides it into slices showing each part's share. Use it when there are no more than five or six slices and "how much of the whole" is what matters. Unlike other charts, data goes on rx.recharts.pie, not on the chart container. data_key is the slice size and name_key is the slice name.

Put "fill" in a dict of the data and only that slice takes that color; otherwise the fill of pie is used.

rx.recharts.pie_chart(
    rx.recharts.pie(
        data=[
            {"name": "Group A", "value": 400},
            {"name": "Group B", "value": 300, "fill": rx.color("teal", 9)},
            {"name": "Group C", "value": 300, "fill": rx.color("orange", 9)},
            {"name": "Group D", "value": 200, "fill": rx.color("plum", 9)},
        ],
        data_key="value",
        name_key="name",
        fill=rx.color("accent", 9),
        label=True,
    ),
    width="100%",
    height=300,
)

Try the props

Make inner_radius greater than 0 and the pie becomes a donut with a hollow center. Radii take a number (pixels) or a percentage string.

Try the props

inner_radius

outer_radius

color

label

label_line

legend_type

rx.recharts.pie_chart(
    rx.recharts.pie(
        data=[
            {"name": "Chrome", "value": 275},
            {"name": "Safari", "value": 150},
            {"name": "Firefox", "value": 100},
            {"name": "Edge", "value": 130},
        ],
        data_key="value",
        name_key="name",
        inner_radius="0%",
        outer_radius="80%",
        fill=rx.color("indigo", 8),
        stroke=rx.color("gray", 1),
        label=True,
        label_line=True,
        legend_type="rect",
    ),
    rx.recharts.graphing_tooltip(),
    rx.recharts.legend(),
    width="100%",
    height=300,
)
label puts values outside the slices, and label_line is the line joining a slice to its value. legend_type is the shape of the legend icon.
PropValuesDefaultUse
pie_chart(width=, height=, margin=)number or percentage / dict"100%"Chart size and margin. data does not go here
pie(data=)list of dictsnoneSlice data
pie(data_key=, name_key=)stringnone / "name"Keys for slice size and name
pie(cx=, cy=)number or percentage"50%"Center position
pie(inner_radius=, outer_radius=)number or percentage0 / "80%"Inner and outer radius. An inner radius above 0 makes a donut
pie(start_angle=, end_angle=)number (degrees)0 / 360Angle range to draw. 180 and 0 make a half circle
pie(padding_angle=, min_angle=)number (degrees)0Gap between slices; minimum angle for small slices
pie(fill=, stroke=)coloraccent 3 / accent 9Slice color and border color
pie(label=, label_line=)True / False / dictFalseValue labels and their connecting lines
pie(legend_type=)"rect" "circle" … "none""rect"Legend icon
pie(is_animation_active=, animation_begin=, animation_duration=, animation_easing=)True / 400 / 1500 / "ease"Entrance animation
cell(fill=, stroke=)colorColor of one slice inside pie
Eventson_click on_mouse_enter on_mouse_leave …Accepted by pie_chart, pie and cell

Overlaying two pies

Two pies are drawn on top of each other around the same center. Make the inner pie small with outer_radius="50%" and hollow out the outer ring with inner_radius="60%", and you can compare two datasets in one picture. padding_angle spaces the slices apart by that many degrees.

rx.recharts.pie_chart(
    rx.recharts.pie(
        data=[
            {"name": "Q1", "value": 400},
            {"name": "Q2", "value": 300},
            {"name": "Q3", "value": 300},
            {"name": "Q4", "value": 200},
        ],
        data_key="value",
        name_key="name",
        fill=rx.color("teal", 8),
        inner_radius="60%",
        padding_angle=5,
    ),
    rx.recharts.pie(
        data=[
            {"name": "Q1", "value": 2400},
            {"name": "Q2", "value": 4567},
            {"name": "Q3", "value": 1398},
            {"name": "Q4", "value": 9800},
        ],
        data_key="value",
        name_key="name",
        fill=rx.color("accent", 8),
        outer_radius="50%",
    ),
    rx.recharts.graphing_tooltip(),
    width="100%",
    height=300,
)

Coloring each slice (cell)

Without putting colors in the data, you can set them by placing one rx.recharts.cell per slice inside pie. Loop over the data with rx.foreach to build the cells and pick the index-th color from a color list. The order of the cells is the order of the slices. Set stroke to the background color with a thick border and gaps appear between the slices.

piechart_data = [
    {"name": "Chrome", "value": 275},
    {"name": "Safari", "value": 150},
    {"name": "Firefox", "value": 100},
    {"name": "Edge", "value": 130},
    {"name": "Other", "value": 60},
]


rx.recharts.pie_chart(
    rx.recharts.pie(
        rx.foreach(
            piechart_data,
            lambda item, index: rx.recharts.cell(
                fill=rx.Var.create(["#6366F1", "#8B5CF6", "#A855F7", "#D946EF", "#EC4899"])[index % 5],
            ),
        ),
        data=piechart_data,
        data_key="value",
        name_key="name",
        inner_radius="40%",
        outer_radius="80%",
        padding_angle=2,
        stroke="#fff",
        custom_attrs={"strokeWidth": 2},
    ),
    rx.recharts.graphing_tooltip(),
    width="100%",
    height=300,
)

Gradient fills

Define radial_gradients in a zero-size rx.el.svg and point to them with url(#id) in each cell's fill. The gradient definitions can sit outside the chart as long as they are on the same page. Give each id a name that is unique on the page.

piechart_data = [
    {"name": "Chrome", "value": 275},
    {"name": "Safari", "value": 150},
    {"name": "Firefox", "value": 100},
    {"name": "Edge", "value": 130},
    {"name": "Other", "value": 60},
]


rx.box(
    rx.el.svg(
        rx.el.svg.defs(
            *[
                rx.el.svg.radial_gradient(
                    rx.el.svg.stop(offset="10%", stop_color=color, stop_opacity=1),
                    rx.el.svg.stop(offset="95%", stop_color=color, stop_opacity=0.6),
                    id=f"piechart_grad_{i}",
                    cx="50%",
                    cy="50%",
                    r="50%",
                )
                for i, color in enumerate(["#6366F1", "#8B5CF6", "#EC4899", "#F59E0B"])
            ],
        ),
        width=0,
        height=0,
    ),
    rx.recharts.pie_chart(
        rx.recharts.pie(
            *[rx.recharts.cell(fill=f"url(#piechart_grad_{i})") for i in range(4)],
            data=piechart_data[:4],
            data_key="value",
            name_key="name",
            inner_radius="60%",
            outer_radius="80%",
            padding_angle=5,
            stroke="#fff",
            custom_attrs={"strokeWidth": 2},
        ),
        rx.recharts.graphing_tooltip(),
        width="100%",
        height=300,
    ),
    width="100%",
)

Data that changes with State

Give the pie's data a State value and the slice sizes follow whenever the numbers change. start_angle=180 and end_angle=0 make a half-circle chart, with the center moved down (cy="80%").

Wood 3

Stone 2

Gold 1

Food 4

from typing import Any


class PieChartStockState(rx.State):
    stock: list[dict[str, Any]] = [
        {"item": "Wood", "count": 3},
        {"item": "Stone", "count": 2},
        {"item": "Gold", "count": 1},
        {"item": "Food", "count": 4},
    ]

    @rx.event
    def change(self, item: str, delta: int):
        self.stock = [
            {**row, "count": max(0, row["count"] + delta)} if row["item"] == item else row
            for row in self.stock
        ]


rx.vstack(
    rx.recharts.pie_chart(
        rx.recharts.pie(
            data=PieChartStockState.stock,
            data_key="count",
            name_key="item",
            cx="50%",
            cy="80%",
            start_angle=180,
            end_angle=0,
            fill=rx.color("accent", 8),
            label=True,
        ),
        rx.recharts.graphing_tooltip(),
        width="100%",
        height=240,
    ),
    rx.flex(
        rx.foreach(
            PieChartStockState.stock,
            lambda row: rx.hstack(
                rx.button("-", size="1", variant="soft", on_click=PieChartStockState.change(row["item"].to(str), -1)),
                rx.text(row["item"], " ", row["count"], size="2"),
                rx.button("+", size="1", variant="soft", on_click=PieChartStockState.change(row["item"].to(str), 1)),
                align="center",
            ),
        ),
        spacing="4",
        wrap="wrap",
    ),
    align="center",
    width="100%",
)

On hover (hover events)

Pass the slice's index to the cell's on_mouse_enter and clear it in on_mouse_leave. The value and name of the hovered slice appear in text laid over the center of the donut. Give the text box pointer_events="none" so the mouse still reaches the slices.

Hover a slice

class PieChartHoverState(rx.State):
    languages: list[dict[str, str | int]] = [
        {"name": "Python", "value": 35},
        {"name": "JavaScript", "value": 25},
        {"name": "Java", "value": 20},
        {"name": "C++", "value": 15},
        {"name": "Ruby", "value": 5},
    ]
    hovered_name: str = ""
    hovered_value: int = 0

    @rx.event
    def enter(self, index: int):
        self.hovered_name = str(self.languages[index]["name"])
        self.hovered_value = int(self.languages[index]["value"])

    @rx.event
    def leave(self):
        self.hovered_name, self.hovered_value = "", 0


rx.box(
    rx.vstack(
        rx.cond(
            PieChartHoverState.hovered_name != "",
            rx.fragment(
                rx.heading(PieChartHoverState.hovered_value, "%", size="7"),
                rx.text(PieChartHoverState.hovered_name, size="2"),
            ),
            rx.text("Hover a slice", size="2", color_scheme="gray"),
        ),
        align="center",
        spacing="1",
        position="absolute",
        top="50%",
        left="50%",
        transform="translate(-50%, -50%)",
        pointer_events="none",
    ),
    rx.recharts.pie_chart(
        rx.recharts.pie(
            rx.foreach(
                PieChartHoverState.languages,
                lambda item, index: rx.recharts.cell(
                    fill=rx.Var.create(["#2B79D1", "#2469B3", "#1E5AA1", "#3D8EE1", "#61A9E4"])[index % 5],
                    on_mouse_enter=PieChartHoverState.enter(index),
                    on_mouse_leave=PieChartHoverState.leave,
                ),
            ),
            data=PieChartHoverState.languages,
            data_key="value",
            name_key="name",
            inner_radius=90,
            stroke="0",
        ),
        width="100%",
        height=300,
    ),
    position="relative",
    width="100%",
)

Filling the order

Five browsers are drawn as a donut, and cell paints each slice a color in a fixed order. The last one, "Other", is gray to tone it down. Values are read from the label, names from the legend and tooltip.

piechart_data = [
    {"name": "Chrome", "value": 275},
    {"name": "Safari", "value": 150},
    {"name": "Firefox", "value": 100},
    {"name": "Edge", "value": 130},
    {"name": "Other", "value": 60},
]


rx.recharts.pie_chart(
    rx.recharts.pie(
        rx.foreach(
            piechart_data,
            lambda item, index: rx.recharts.cell(
                fill=rx.Var.create([
                    rx.color("indigo", 9), rx.color("teal", 9), rx.color("orange", 9),
                    rx.color("plum", 9), rx.color("gray", 8),
                ])[index],
            ),
        ),
        data=piechart_data,
        data_key="value",
        name_key="name",
        inner_radius="55%",
        outer_radius="80%",
        padding_angle=2,
        stroke=rx.color("gray", 1),
        label=True,
    ),
    rx.recharts.graphing_tooltip(),
    rx.recharts.legend(),
    width="100%",
    height=320,
)

Workshop notes

  • data goes on pie, not on pie_chart. data_key is the size, name_key is the name.

  • inner_radius > 0 makes a donut; start_angle and end_angle make a half circle.

  • Slice colors come from a "fill" key in the data, or from rx.recharts.cell placed inside pie with foreach.

  • Two pies overlap around the same center. Divide the space with outer_radius and inner_radius.

  • A cell's on_mouse_enter(index) can store the hovered slice in State.

← Previous

Charts · Composed Chart

Next →

Charts · Radar Chart

Get news about new lessons

We'll email you when this course gets new lessons or a follow-up course opens.

We collect: your email address · Purpose: news about new lessons or the launch of this course · Kept until: you ask us to remove it. We never use it for anything else or share it. Reply to any notice email and we'll delete it right away.

Questions & answers

Ask anything about this lesson. Questions from every language version of this page appear together.

Shown as plain text, without links or formatting.

R

ReflexKR

Learn Python and Reflex by doing. Every example was run and verified on Reflex 0.10.0.

ReflexKR is free, with no banner ads. If it helped you, you can support it with a coffee. ☕ Buy us a coffee

Affiliate disclosure
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex