From 3e2de46c5e221e076da48f990b397cdacdef33a5 Mon Sep 17 00:00:00 2001 From: amsraman Date: Wed, 15 Jul 2026 16:05:12 -0700 Subject: [PATCH 1/5] docs: recharts chart techniques from AI builder field notes Gradient fills (area/bar/pie), rounded bars, axis label styling, per-slice cell coloring, series shapes, tick/grid styling, and click-drilldown + hover-event recipes. All demos exec-verified. Co-Authored-By: Claude Fable 5 --- docs/library/graphing/charts/areachart.md | 44 +++++ docs/library/graphing/charts/barchart.md | 160 +++++++++++++++++ docs/library/graphing/charts/linechart.md | 48 +++++ docs/library/graphing/charts/piechart.md | 170 ++++++++++++++++++ docs/library/graphing/charts/scatterchart.md | 48 +++++ docs/library/graphing/general/axis.md | 45 +++++ .../library/graphing/general/cartesiangrid.md | 36 ++++ docs/library/graphing/general/label.md | 46 +++++ 8 files changed, 597 insertions(+) diff --git a/docs/library/graphing/charts/areachart.md b/docs/library/graphing/charts/areachart.md index 06573b36c44..cb2bf02c60e 100644 --- a/docs/library/graphing/charts/areachart.md +++ b/docs/library/graphing/charts/areachart.md @@ -137,6 +137,50 @@ def area_stack(): ) ``` +## Gradient Fill + +An SVG linear gradient can give the filled area a smooth fade-out effect. Define the gradient inside an `rx.el.svg.defs` block as the first child of the chart, then reference it from the area's `fill` prop with `"url(#gradient-id)"`. + +```python demo graphing +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 area_gradient(): + return rx.recharts.area_chart( + rx.el.svg.defs( + rx.el.svg.linear_gradient( + rx.el.svg.stop(offset="5%", stop_color="#8884d8", stop_opacity=0.8), + rx.el.svg.stop(offset="95%", stop_color="#8884d8", stop_opacity=0), + id="area_gradient", + x1=0, + x2=0, + y1=0, + y2=1, + ), + ), + rx.recharts.area( + data_key="uv", + stroke="#8884d8", + fill="url(#area_gradient)", + type_="natural", + ), + rx.recharts.x_axis(data_key="name"), + rx.recharts.y_axis(), + rx.recharts.graphing_tooltip(), + data=data, + width="100%", + height=300, + ) +``` + ## Multiple Axis Multiple axes can be used for displaying different data series with varying scales or units on the same chart. This allows for a more comprehensive comparison and analysis of the data. diff --git a/docs/library/graphing/charts/barchart.md b/docs/library/graphing/charts/barchart.md index 86fa2fe422f..32c14dbe09b 100644 --- a/docs/library/graphing/charts/barchart.md +++ b/docs/library/graphing/charts/barchart.md @@ -193,6 +193,81 @@ def bar_with_state(): ) ``` +## 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 demo exec +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)), + 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. @@ -226,6 +301,91 @@ def bar_features(): ) ``` +## 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 demo graphing +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 demo graphing +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, 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=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. diff --git a/docs/library/graphing/charts/linechart.md b/docs/library/graphing/charts/linechart.md index e0b8143de68..47a32dc7a64 100644 --- a/docs/library/graphing/charts/linechart.md +++ b/docs/library/graphing/charts/linechart.md @@ -87,6 +87,54 @@ def line_features(): ) ``` +## Axis Labels + +Nest an `rx.recharts.label` inside `rx.recharts.x_axis` or `rx.recharts.y_axis` to title the axes. Use the `position` prop to place the label, and rotate the y-axis label with `custom_attrs={"angle": 270}`. Give the x-axis some extra `height` and the chart a left `margin` so the labels have room. The axes here also use `axis_line=False` and `tick_line=False` for a cleaner look. + +```python demo graphing +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 line_axis_labels(): + return rx.recharts.line_chart( + rx.recharts.line( + data_key="uv", + stroke=rx.color("accent", 9), + stroke_width=2, + type_="natural", + ), + rx.recharts.x_axis( + rx.recharts.label(value="Page", position="center"), + data_key="name", + height=60, + axis_line=False, + tick_line=False, + ), + rx.recharts.y_axis( + rx.recharts.label( + value="Visits", + position="left", + custom_attrs={"angle": 270}, + ), + axis_line=False, + tick_line=False, + ), + rx.recharts.graphing_tooltip(), + data=data, + margin={"left": 20, "right": 20, "top": 20}, + width="100%", + height=300, + ) +``` + ## Layout The `layout` prop allows you to set the orientation of the graph to be vertical or horizontal. The `margin` prop defines the spacing around the graph, diff --git a/docs/library/graphing/charts/piechart.md b/docs/library/graphing/charts/piechart.md index 24d17a12c3c..201fbbde3e2 100644 --- a/docs/library/graphing/charts/piechart.md +++ b/docs/library/graphing/charts/piechart.md @@ -88,6 +88,108 @@ def pie_double(): ) ``` +## Coloring Slices Individually + +Instead of storing a `fill` color in every data entry, you can pass `rx.recharts.cell` components as children of `rx.recharts.pie` — one cell per slice. Using `rx.foreach` with the index argument lets you cycle through a color palette, so the palette lives in one place and works for any number of slices. This example also combines `inner_radius` and `padding_angle` to render the pie as a donut, and uses `stroke` and `stroke_width` to draw a separator around each slice. + +```python demo graphing +data = [ + {"browser": "Chrome", "visitors": 275}, + {"browser": "Firefox", "visitors": 150}, + {"browser": "Safari", "visitors": 100}, + {"browser": "Opera", "visitors": 130}, + {"browser": "Edge", "visitors": 140}, +] + +colors = ["#6366F1", "#8B5CF6", "#A855F7", "#D946EF", "#EC4899"] + + +def pie_cells(): + return rx.recharts.pie_chart( + rx.recharts.pie( + rx.foreach( + data, + lambda item, index: rx.recharts.cell( + fill=rx.Var.create(colors)[index % len(colors)], + ), + ), + data=data, + data_key="visitors", + name_key="browser", + inner_radius="40%", + outer_radius="80%", + padding_angle=2, + stroke="#fff", + stroke_width=2, + ), + rx.recharts.graphing_tooltip(), + width="100%", + height=300, + ) +``` + +## Gradient Fills + +Slices can also be filled with SVG gradients. Define the gradients inside a hidden `rx.el.svg` element, then reference each one from a cell's `fill` prop with `url(#gradient_id)`. A radial gradient works well for pie slices since it follows the circular shape. + +```python demo graphing +data = [ + {"name": "Product A", "value": 400}, + {"name": "Product B", "value": 300}, + {"name": "Product C", "value": 300}, + {"name": "Product D", "value": 200}, +] + +gradients = [ + ("#6366F1", "pie_grad_a"), + ("#8B5CF6", "pie_grad_b"), + ("#EC4899", "pie_grad_c"), + ("#F59E0B", "pie_grad_d"), +] + + +def create_gradient(color: str, id: str) -> rx.Component: + return 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=id, + cx="50%", + cy="50%", + r="50%", + fx="50%", + fy="50%", + ) + + +def pie_gradient(): + return rx.box( + rx.el.svg( + rx.el.svg.defs( + *[create_gradient(color, id) for color, id in gradients], + ), + width=0, + height=0, + ), + rx.recharts.pie_chart( + rx.recharts.pie( + *[rx.recharts.cell(fill=f"url(#{id})") for _, id in gradients], + data=data, + data_key="value", + name_key="name", + inner_radius="60%", + outer_radius="80%", + padding_angle=5, + stroke="#fff", + stroke_width=2, + ), + rx.recharts.graphing_tooltip(), + width="100%", + height=300, + ), + width="100%", + ) +``` + ## Dynamic Data Chart data tied to a State var causes the chart to automatically update when the @@ -157,6 +259,74 @@ def dynamic_pie_example(): ) ``` +## Hover Events + +Charts can react to the mouse with `on_mouse_enter` and `on_mouse_leave`. Like click events, these are best attached to `rx.recharts.cell` components rendered with `rx.foreach`, so each handler is bound to the index of its data point at render time and the event handler can look up the hovered item in state. + +This example shows the hovered slice's name and value in the center of a donut chart, and clears them when the mouse leaves. + +```python demo exec +PIE_HOVER_COLORS = ["#2B79D1", "#2469B3", "#1E5AA1", "#3D8EE1", "#61A9E4"] + + +class PieHoverState(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 handle_mouse_enter(self, index: int): + self.hovered_name = str(self.languages[index]["name"]) + self.hovered_value = int(self.languages[index]["value"]) + + @rx.event + def handle_mouse_leave(self): + self.hovered_name, self.hovered_value = "", 0 + + +def pie_hover(): + return rx.box( + rx.vstack( + rx.heading(PieHoverState.hovered_value, size="8"), + rx.text(PieHoverState.hovered_name, size="2"), + align="center", + spacing="1", + position="absolute", + top="50%", + left="50%", + transform="translate(-50%, -50%)", + ), + rx.recharts.pie_chart( + rx.recharts.pie( + rx.foreach( + PieHoverState.languages, + lambda item, index: rx.recharts.cell( + fill=rx.Var.create(PIE_HOVER_COLORS)[index], + on_mouse_enter=PieHoverState.handle_mouse_enter(index), + on_mouse_leave=PieHoverState.handle_mouse_leave, + ), + ), + data=PieHoverState.languages, + data_key="value", + name_key="name", + inner_radius=90, + stroke="0", + ), + width="100%", + height=300, + ), + position="relative", + width="100%", + ) +``` + ## Related Charts Explore more chart types you can build with Reflex and Recharts in pure Python: diff --git a/docs/library/graphing/charts/scatterchart.md b/docs/library/graphing/charts/scatterchart.md index 3741a032f8c..6085341a8e1 100644 --- a/docs/library/graphing/charts/scatterchart.md +++ b/docs/library/graphing/charts/scatterchart.md @@ -210,6 +210,54 @@ def scatter_shape(): ) ``` +## 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. diff --git a/docs/library/graphing/general/axis.md b/docs/library/graphing/general/axis.md index f210741e644..f2f79de7b73 100644 --- a/docs/library/graphing/general/axis.md +++ b/docs/library/graphing/general/axis.md @@ -95,6 +95,51 @@ def multi_axis(): ) ``` +## Styling Axis Ticks + +Axes can be styled for a cleaner, more minimal look. Setting `axis_line=False` and `tick_line=False` removes the axis and tick lines, leaving only the tick labels. The `tick_size` prop controls the gap between the axis and its tick labels, and `interval="preserveStartEnd"` ensures the first and last ticks are always shown even when labels are dropped to avoid overlap. Font styling that is not exposed as a prop, such as the tick font size, can be passed to the underlying Recharts axis with `custom_attrs`. + +```python demo graphing +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 axis_styled_ticks(): + return rx.recharts.area_chart( + rx.recharts.area( + data_key="uv", + stroke=rx.color("accent", 9), + fill=rx.color("accent", 8), + ), + rx.recharts.x_axis( + data_key="name", + axis_line=False, + tick_line=False, + tick_size=10, + interval="preserveStartEnd", + custom_attrs={"fontSize": "12px"}, + ), + rx.recharts.y_axis( + axis_line=False, + tick_line=False, + tick_size=10, + interval="preserveStartEnd", + custom_attrs={"fontSize": "12px"}, + ), + data=data, + width="100%", + height=300, + margin={"left": 20, "right": 20, "top": 25}, + ) +``` + ## Choosing Location of Labels for Axes The axes `label` can take several positions. The example below allows you to try out different locations for the x and y axis labels. diff --git a/docs/library/graphing/general/cartesiangrid.md b/docs/library/graphing/general/cartesiangrid.md index f332e01121c..1654bdd6c72 100644 --- a/docs/library/graphing/general/cartesiangrid.md +++ b/docs/library/graphing/general/cartesiangrid.md @@ -79,6 +79,42 @@ def cgrid_hidden(): ) ``` +## Styling the Grid + +The opacity and color of the grid lines can be customized with the `class_name` prop. Lowering the opacity keeps the grid available as a visual reference without competing with the data itself. Combined with showing only horizontal lines, this gives charts a subtle, modern look. + +```python demo graphing +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 cgrid_styled(): + return rx.recharts.area_chart( + rx.recharts.area( + data_key="uv", + stroke=rx.color("accent", 9), + fill=rx.color("accent", 8), + ), + rx.recharts.x_axis(data_key="name"), + rx.recharts.y_axis(), + rx.recharts.cartesian_grid( + horizontal=True, + vertical=False, + class_name="opacity-25", + ), + data=data, + width="100%", + height=300, + ) +``` + ## Custom Grid Lines The `horizontal_points` and `vertical_points` props allow you to specify custom grid lines on the chart, offering fine-grained control over the grid's appearance. diff --git a/docs/library/graphing/general/label.md b/docs/library/graphing/general/label.md index a46a73b03a3..5d7df75feb6 100644 --- a/docs/library/graphing/general/label.md +++ b/docs/library/graphing/general/label.md @@ -104,3 +104,49 @@ def label_list(): height=300, ) ``` + +## Styling Labels + +Styling that is not exposed as a prop on `rx.recharts.label` can be passed through to the underlying Recharts label with `custom_attrs`. This is useful for setting the font size and weight of an axis label, or rotating a y-axis label with `angle` so it reads vertically along the axis. + +```python demo graphing +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": 5800, "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 label_styled(): + return rx.recharts.area_chart( + rx.recharts.area( + data_key="uv", + stroke=rx.color("accent", 9), + fill=rx.color("accent", 8), + ), + rx.recharts.x_axis( + rx.recharts.label( + value="Pages", + position="center", + custom_attrs={"fontSize": "12px", "fontWeight": "700"}, + ), + data_key="name", + height=60, + ), + rx.recharts.y_axis( + rx.recharts.label( + value="Views", + position="left", + custom_attrs={"angle": 270, "fontSize": "12px", "fontWeight": "700"}, + ), + ), + data=data, + margin={"left": 20, "right": 20, "top": 25}, + width="100%", + height=300, + ) +``` From 532ee46a1ee743ce70e6fcdbf50e798b90edc877 Mon Sep 17 00:00:00 2001 From: Aditya Sundaram Date: Thu, 16 Jul 2026 17:21:56 -0700 Subject: [PATCH 2/5] docs(piechart): guard PIE_HOVER_COLORS index with modulo; rename id param to gradient_id --- docs/library/graphing/charts/piechart.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/library/graphing/charts/piechart.md b/docs/library/graphing/charts/piechart.md index 201fbbde3e2..5cba39c1723 100644 --- a/docs/library/graphing/charts/piechart.md +++ b/docs/library/graphing/charts/piechart.md @@ -148,11 +148,11 @@ gradients = [ ] -def create_gradient(color: str, id: str) -> rx.Component: +def create_gradient(color: str, gradient_id: str) -> rx.Component: return 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=id, + id=gradient_id, cx="50%", cy="50%", r="50%", @@ -165,14 +165,20 @@ def pie_gradient(): return rx.box( rx.el.svg( rx.el.svg.defs( - *[create_gradient(color, id) for color, id in gradients], + *[ + create_gradient(color, gradient_id) + for color, gradient_id in gradients + ], ), width=0, height=0, ), rx.recharts.pie_chart( rx.recharts.pie( - *[rx.recharts.cell(fill=f"url(#{id})") for _, id in gradients], + *[ + rx.recharts.cell(fill=f"url(#{gradient_id})") + for _, gradient_id in gradients + ], data=data, data_key="value", name_key="name", @@ -308,7 +314,9 @@ def pie_hover(): rx.foreach( PieHoverState.languages, lambda item, index: rx.recharts.cell( - fill=rx.Var.create(PIE_HOVER_COLORS)[index], + fill=rx.Var.create(PIE_HOVER_COLORS)[ + index % len(PIE_HOVER_COLORS) + ], on_mouse_enter=PieHoverState.handle_mouse_enter(index), on_mouse_leave=PieHoverState.handle_mouse_leave, ), From dbf54183f914286e3ce7da916b5c8fe204b5c97c Mon Sep 17 00:00:00 2001 From: Aditya Sundaram Date: Thu, 16 Jul 2026 17:21:57 -0700 Subject: [PATCH 3/5] docs(barchart): rename create_bar_gradient id param to gradient_id --- docs/library/graphing/charts/barchart.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/library/graphing/charts/barchart.md b/docs/library/graphing/charts/barchart.md index 32c14dbe09b..67054ad6610 100644 --- a/docs/library/graphing/charts/barchart.md +++ b/docs/library/graphing/charts/barchart.md @@ -348,11 +348,11 @@ data = [ ] -def create_bar_gradient(color: str, id: str) -> rx.Component: +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=id, + id=gradient_id, x1=0, y1=0, x2=0, From a6b014b44ac36e10abfeefb29f6b728a7c6607fb Mon Sep 17 00:00:00 2001 From: Aditya Sundaram Date: Thu, 16 Jul 2026 17:39:06 -0700 Subject: [PATCH 4/5] docs: split graphing demo blocks on the def keyword, not the 'def' substring The graphing demo renderer separated the data prelude from the chart function with content.rpartition('def'), which also matches inside rx.el.svg.defs(...). The new gradient chart examples use defs, so the split landed mid-identifier and handed ruff_format two truncated, unparseable halves, failing the docs double-eval tests. Split on a line-starting 'def ' instead. --- docs/app/reflex_docs/docgen_pipeline.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/app/reflex_docs/docgen_pipeline.py b/docs/app/reflex_docs/docgen_pipeline.py index 998dce09bcf..4ec70c7f9ce 100644 --- a/docs/app/reflex_docs/docgen_pipeline.py +++ b/docs/app/reflex_docs/docgen_pipeline.py @@ -437,8 +437,8 @@ def _render_demo(self, content: str, flags: set[str]) -> rx.Component: comp = self._exec_and_get_last_callable(content) elif "graphing" in flags: comp = self._exec_and_get_last_callable(content) - parts = content.rpartition("def") - data, code = parts[0], parts[1] + parts[2] + before, sep, after = content.rpartition("\ndef ") + data, code = (before, "def " + after) if sep else ("", content) return docgraphing(code, comp=comp, data=data) elif "box" in flags: comp = eval(content, self.env, self.env) @@ -473,8 +473,8 @@ def _render_demo_only(self, content: str, flags: set[str]) -> rx.Component: comp = self._exec_and_get_last_callable(content) elif "graphing" in flags: comp = self._exec_and_get_last_callable(content) - parts = content.rpartition("def") - data, code = parts[0], parts[1] + parts[2] + before, sep, after = content.rpartition("\ndef ") + data, code = (before, "def " + after) if sep else ("", content) return docgraphing(code, comp=comp, data=data) elif "box" in flags: comp = eval(content, self.env, self.env) From 3f2b28801f6a2dc4b232d8025dcc861088a47e32 Mon Sep 17 00:00:00 2001 From: amsraman Date: Fri, 17 Jul 2026 16:09:30 -0700 Subject: [PATCH 5/5] docs: forward strokeWidth/cursor via custom_attrs; hide pie hover label until hover - Pie separator width and the drill-down pointer cursor were passed as stroke_width / cursor props, which Reflex emits into wrapperStyle (dead) rather than the underlying SVG. Route both through custom_attrs so they actually apply. - The pie hover example rendered hovered_value unconditionally, showing a '0' in the donut center before any hover. Gate the center label on a hovered slice, with a 'Hover a slice' hint otherwise. --- docs/library/graphing/charts/barchart.md | 2 +- docs/library/graphing/charts/piechart.md | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/library/graphing/charts/barchart.md b/docs/library/graphing/charts/barchart.md index 67054ad6610..2d5be2e6be5 100644 --- a/docs/library/graphing/charts/barchart.md +++ b/docs/library/graphing/charts/barchart.md @@ -233,7 +233,7 @@ 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)), - cursor="pointer", + custom_attrs={"cursor": "pointer"}, fill=rx.cond( BarDrilldownState.drilled_genre == item["name"], "#1D4ED8", diff --git a/docs/library/graphing/charts/piechart.md b/docs/library/graphing/charts/piechart.md index 5cba39c1723..8f329d16cc9 100644 --- a/docs/library/graphing/charts/piechart.md +++ b/docs/library/graphing/charts/piechart.md @@ -90,7 +90,7 @@ def pie_double(): ## Coloring Slices Individually -Instead of storing a `fill` color in every data entry, you can pass `rx.recharts.cell` components as children of `rx.recharts.pie` — one cell per slice. Using `rx.foreach` with the index argument lets you cycle through a color palette, so the palette lives in one place and works for any number of slices. This example also combines `inner_radius` and `padding_angle` to render the pie as a donut, and uses `stroke` and `stroke_width` to draw a separator around each slice. +Instead of storing a `fill` color in every data entry, you can pass `rx.recharts.cell` components as children of `rx.recharts.pie` — one cell per slice. Using `rx.foreach` with the index argument lets you cycle through a color palette, so the palette lives in one place and works for any number of slices. This example also combines `inner_radius` and `padding_angle` to render the pie as a donut, and uses `stroke` with `custom_attrs={"strokeWidth": 2}` to draw a separator around each slice. (Passing `stroke_width` directly is not forwarded to the underlying SVG element, so the width goes through `custom_attrs`.) ```python demo graphing data = [ @@ -120,7 +120,7 @@ def pie_cells(): outer_radius="80%", padding_angle=2, stroke="#fff", - stroke_width=2, + custom_attrs={"strokeWidth": 2}, ), rx.recharts.graphing_tooltip(), width="100%", @@ -186,7 +186,7 @@ def pie_gradient(): outer_radius="80%", padding_angle=5, stroke="#fff", - stroke_width=2, + custom_attrs={"strokeWidth": 2}, ), rx.recharts.graphing_tooltip(), width="100%", @@ -300,8 +300,14 @@ class PieHoverState(rx.State): def pie_hover(): return rx.box( rx.vstack( - rx.heading(PieHoverState.hovered_value, size="8"), - rx.text(PieHoverState.hovered_name, size="2"), + rx.cond( + PieHoverState.hovered_name != "", + rx.fragment( + rx.heading(PieHoverState.hovered_value, size="8"), + rx.text(PieHoverState.hovered_name, size="2"), + ), + rx.text("Hover a slice", size="2", color_scheme="gray"), + ), align="center", spacing="1", position="absolute",