Navigation
Pages
Home
Docs
Components
Dashboards
Blocks
Typeset
Create
Getting Started
Introduction
Installation
Skills
CLI
JavaScript
dev.py
Changelog
llms.txt
Utilities
Markdown
Scroll Fade
Shimmer
Resources
Chart
Client State Var
Events & Actions
State Structure
Theming
Typeset
Charts
Area Chart
Bar Chart
Line Chart
Components
Accordion
Attachment
Avatar
Badge
Bubble
Button
Button Group
Card
Checkbox
Dialog
Field
Frame
Input
Input Group
Marker
Menu
Message
Select
Separator
Spinner
Switch
Table
Tabs
Textarea
Typography
Blocks
Building Blocks for Dashboards
Clean, modern building blocks for Reflex dashboards. Copy and paste into your apps. Open Source. Extensible.
Filter Blocks
Area Charts
Filled line charts for showing trends over time.
Actual costs vs. potential costs
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.
$540,690
Actual costs
$422,300
Potential costs
-21.9%
Potential savings (%)
$118,390
Potential savings ($)
import reflex as rx
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
data = [
{"date": "Jan 23", "Actual costs": 42340, "Potential costs": 32330},
{"date": "Feb 23", "Actual costs": 50120, "Potential costs": 40100},
{"date": "Mar 23", "Actual costs": 45190, "Potential costs": 38240},
{"date": "Apr 23", "Actual costs": 56420, "Potential costs": 31200},
{"date": "May 23", "Actual costs": 40420, "Potential costs": 34900},
{"date": "Jun 23", "Actual costs": 47010, "Potential costs": 36800},
{"date": "Jul 23", "Actual costs": 47490, "Potential costs": 34560},
{"date": "Aug 23", "Actual costs": 39610, "Potential costs": 31260},
{"date": "Sep 23", "Actual costs": 45860, "Potential costs": 29240},
{"date": "Oct 23", "Actual costs": 50910, "Potential costs": 31220},
{"date": "Nov 23", "Actual costs": 49190, "Potential costs": 33020},
{"date": "Dec 23", "Actual costs": 55190, "Potential costs": 36090},
]
summary = [
("Actual costs", "$540,690", "chart-1"),
("Potential costs", "$422,300", "chart-2"),
("Potential savings (%)", "-21.9%", None),
("Potential savings ($)", "$118,390", None),
]
def _gradient(id_: str, color: str) -> rx.Component:
return rx.el.svg.linear_gradient(
rx.el.svg.stop(stop_color=f"var(--{color})", offset="5%", stop_opacity=0.2),
rx.el.svg.stop(stop_color=f"var(--{color})", offset="95%", stop_opacity=0.2),
x1=0,
x2=0,
y1=0,
y2=1,
id=id_,
)
def _area(data_key: str, color: str) -> rx.Component:
return rx.recharts.area(
data_key=data_key,
fill=f"url(#{data_key.replace(' ', '_')})",
stroke=f"var(--{color})",
stroke_width=2,
is_animation_active=False,
active_dot={"fill": f"var(--{color})", "stroke": f"var(--{color})"},
dot=False,
)
def _chart(show_y_axis: bool) -> rx.Component:
return rx.recharts.area_chart(
rx.el.svg.defs(
_gradient("Actual_costs", "chart-1"),
_gradient("Potential_costs", "chart-2"),
),
chart_tooltip(),
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
interval="preserveStartEnd",
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
),
rx.recharts.y_axis(
width=40,
tick_line=False,
axis_line=False,
hide=not show_y_axis,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
),
_area("Actual costs", "chart-1"),
_area("Potential costs", "chart-2"),
data=data,
width="100%",
height=288,
)
def area_chart_01():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Actual costs vs. potential costs",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.",
class_name="text-sm text-muted-foreground",
),
),
rx.el.div(
rx.el.ul(
*[
rx.el.li(
rx.el.div(
rx.el.span(
class_name=f"w-1 h-6 shrink-0 rounded-sm bg-{color}"
)
if color
else None,
rx.el.p(total, class_name="text-lg font-semibold"),
class_name="flex items-center gap-2",
),
rx.el.p(
category,
class_name="text-sm text-muted-foreground "
+ ("pl-3" if color else ""),
),
)
for category, total, color in summary
],
class_name="grid grid-cols-2 lg:grid-cols-4 gap-6 mb-8",
),
rx.el.div(_chart(show_y_axis=False), class_name="sm:hidden"),
rx.el.div(_chart(show_y_axis=True), class_name="hidden sm:block"),
),
class_name=chart_tooltip_content([1, 2], "square")
+ " w-full flex flex-col gap-6",
)
Follower metrics
More power? Upgrade to get more insights.
- Organic3,273
- Sponsored120
import reflex as rx
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
data = [
{"date": "Jan 23", "Organic": 232, "Sponsored": 0},
{"date": "Feb 23", "Organic": 241, "Sponsored": 0},
{"date": "Mar 23", "Organic": 291, "Sponsored": 0},
{"date": "Apr 23", "Organic": 101, "Sponsored": 0},
{"date": "May 23", "Organic": 318, "Sponsored": 0},
{"date": "Jun 23", "Organic": 205, "Sponsored": 0},
{"date": "Jul 23", "Organic": 372, "Sponsored": 0},
{"date": "Aug 23", "Organic": 341, "Sponsored": 0},
{"date": "Sep 23", "Organic": 387, "Sponsored": 120},
{"date": "Oct 23", "Organic": 220, "Sponsored": 0},
{"date": "Nov 23", "Organic": 372, "Sponsored": 0},
{"date": "Dec 23", "Organic": 321, "Sponsored": 0},
]
summary = [
("Organic", "3,273", "chart-1"),
("Sponsored", "120", "chart-2"),
]
def _gradient(id_: str, color: str) -> rx.Component:
return rx.el.svg.linear_gradient(
rx.el.svg.stop(stop_color=f"var(--{color})", offset="5%", stop_opacity=0.2),
rx.el.svg.stop(stop_color=f"var(--{color})", offset="95%", stop_opacity=0.2),
x1=0,
x2=0,
y1=0,
y2=1,
id=id_,
)
def _area(data_key: str, color: str) -> rx.Component:
return rx.recharts.area(
data_key=data_key,
fill=f"url(#{data_key})",
stroke=f"var(--{color})",
stroke_width=2,
is_animation_active=False,
dot=False,
active_dot={"fill": f"var(--{color})", "stroke": f"var(--{color})"},
)
def _chart(show_y_axis: bool, start_end_only: bool = False) -> rx.Component:
return rx.recharts.area_chart(
rx.el.svg.defs(
_gradient("Organic", "chart-1"),
_gradient("Sponsored", "chart-2"),
),
chart_tooltip(),
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
interval="preserveStartEnd",
tick_count=2 if start_end_only else None,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
),
rx.recharts.y_axis(
width=30,
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
hide=not show_y_axis,
),
_area("Organic", "chart-1"),
_area("Sponsored", "chart-2"),
data=data,
width="100%",
height=220,
)
def area_chart_02():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Follower metrics",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
"More power? Upgrade to get more insights.",
class_name="text-sm text-muted-foreground",
),
),
rx.el.div(
rx.el.div(
_chart(show_y_axis=False, start_end_only=True), class_name="sm:hidden"
),
rx.el.div(
_chart(show_y_axis=True, start_end_only=False),
class_name="hidden sm:block",
),
rx.el.ul(
*[
rx.el.li(
rx.el.div(
rx.el.span(class_name=f"h-0.5 w-3 bg-{color}"),
rx.el.span(label, class_name="text-sm"),
class_name="flex items-center gap-2",
),
rx.el.span(value, class_name="text-sm font-medium"),
class_name="flex items-center justify-between py-2 border-b border-input last:border-0",
)
for label, value, color in summary
],
class_name="mt-4 w-full",
),
),
class_name=chart_tooltip_content([1, 2], "square")
+ " w-full flex flex-col gap-6",
)
Bar Charts
Categorical comparisons across groups.
Sales overview
Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
This Year
import reflex as rx
from reflex.experimental import ClientStateVar
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
from components.ui.checkbox import checkbox
from components.ui.field import field
ShowComparison = ClientStateVar.create("show_comparison", False)
data = [
{"date": "Jan 23", "This Year": 68560, "Last Year": 28560},
{"date": "Feb 23", "This Year": 70320, "Last Year": 30320},
{"date": "Mar 23", "This Year": 80233, "Last Year": 70233},
{"date": "Apr 23", "This Year": 55123, "Last Year": 45123},
{"date": "May 23", "This Year": 56000, "Last Year": 80600},
{"date": "Jun 23", "This Year": 100000, "Last Year": 85390},
{"date": "Jul 23", "This Year": 85390, "Last Year": 45340},
{"date": "Aug 23", "This Year": 80100, "Last Year": 70120},
{"date": "Sep 23", "This Year": 75090, "Last Year": 69450},
{"date": "Oct 23", "This Year": 71080, "Last Year": 63345},
{"date": "Nov 23", "This Year": 61210, "Last Year": 100330},
{"date": "Dec 23", "This Year": 60143, "Last Year": 45321},
]
def _chart(show_y_axis: bool) -> rx.Component:
return rx.recharts.bar_chart(
chart_tooltip(),
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
interval="preserveStartEnd",
),
rx.recharts.y_axis(
width=55,
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
hide=not show_y_axis,
),
rx.recharts.bar(
data_key="This Year",
fill="var(--chart-1)",
is_animation_active=False,
max_bar_size=40,
),
rx.cond(
ShowComparison.value,
rx.recharts.bar(
data_key="Last Year",
fill="var(--chart-2)",
is_animation_active=False,
max_bar_size=40,
),
),
data=data,
width="100%",
height=250,
)
def bar_chart_01():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Sales overview",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
class_name="text-sm text-muted-foreground",
),
),
rx.el.div(
rx.cond(
ShowComparison.value,
rx.el.div(
rx.el.div(class_name="w-3 h-3 bg-chart-2"),
"Last Year",
class_name="text-sm flex flex-row gap-x-2 items-center",
),
),
rx.el.div(
rx.el.div(class_name="w-3 h-3 bg-chart-1"),
"This Year",
class_name="text-sm flex flex-row gap-x-2 items-center",
),
class_name="flex flex-row gap-x-2 justify-end items-center",
),
rx.el.div(
rx.el.div(_chart(show_y_axis=False), class_name="sm:hidden"),
rx.el.div(_chart(show_y_axis=True), class_name="hidden sm:block"),
),
rx.el.div(
field.root(
checkbox.root(
checkbox.indicator(),
id="terms-checkbox-basic",
on_click=ShowComparison.set_value(~ShowComparison.value),
),
field.label(
"Show same period last year",
html_for="terms-checkbox-basic",
),
orientation="horizontal",
),
),
class_name=chart_tooltip_content([1, 2], "square")
+ " w-full flex flex-col gap-6",
)
Sales overview
Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
This year
$0.8M
+16%Last year
$0.7M
import reflex as rx
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
data = [
{"date": "Jan 23", "This Year": 68560, "Last Year": 28560},
{"date": "Feb 23", "This Year": 70320, "Last Year": 30320},
{"date": "Mar 23", "This Year": 80233, "Last Year": 70233},
{"date": "Apr 23", "This Year": 55123, "Last Year": 45123},
{"date": "May 23", "This Year": 56000, "Last Year": 80600},
{"date": "Jun 23", "This Year": 100000, "Last Year": 85390},
{"date": "Jul 23", "This Year": 85390, "Last Year": 45340},
{"date": "Aug 23", "This Year": 80100, "Last Year": 70120},
{"date": "Sep 23", "This Year": 75090, "Last Year": 69450},
{"date": "Oct 23", "This Year": 71080, "Last Year": 63345},
{"date": "Nov 23", "This Year": 61210, "Last Year": 100330},
{"date": "Dec 23", "This Year": 60143, "Last Year": 45321},
]
def _chart(show_y_axis: bool) -> rx.Component:
return rx.recharts.bar_chart(
chart_tooltip(),
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
interval="preserveStartEnd",
),
rx.recharts.y_axis(
width=50,
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
hide=not show_y_axis,
),
rx.recharts.bar(
data_key="Last Year",
fill="var(--chart-2)",
is_animation_active=False,
),
rx.recharts.bar(
data_key="This Year",
fill="var(--chart-1)",
is_animation_active=False,
),
data=data,
width="100%",
height=300,
)
def bar_chart_02():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Sales overview",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr.",
class_name="text-sm text-muted-foreground",
),
),
rx.el.ul(
rx.el.li(
rx.el.div(
rx.el.div(class_name="w-3 h-3 rounded-[3px] bg-chart-1"),
rx.el.p("This year", class_name="text-sm text-muted-foreground"),
class_name="flex items-center gap-1.5",
),
rx.el.div(
rx.el.p("$0.8M", class_name="mt-0.5 text-base font-semibold"),
rx.el.span(
"+16%",
class_name="rounded px-1.5 py-0.5 text-xs font-medium bg-muted text-muted-foreground",
),
class_name="flex items-center gap-1.5",
),
),
rx.el.li(
rx.el.div(
rx.el.div(class_name="w-3 h-3 rounded-[3px] bg-chart-2"),
rx.el.p("Last year", class_name="text-sm text-muted-foreground"),
class_name="flex items-center gap-1.5",
),
rx.el.p("$0.7M", class_name="mt-0.5 text-base font-semibold"),
),
class_name="flex gap-10",
),
rx.el.div(
rx.el.div(_chart(show_y_axis=False), class_name="sm:hidden"),
rx.el.div(_chart(show_y_axis=True), class_name="hidden sm:block"),
),
class_name=chart_tooltip_content([1, 2], "square")
+ " w-full flex flex-col gap-4",
)
Average BPM
134bpm
import reflex as rx
from reflex.experimental import ClientStateVar
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
data = [
{"date": "Jan 23", "Running": 167, "Cycling": 145},
{"date": "Feb 23", "Running": 125, "Cycling": 110},
{"date": "Mar 23", "Running": 156, "Cycling": 149},
{"date": "Apr 23", "Running": 165, "Cycling": 112},
{"date": "May 23", "Running": 153, "Cycling": 138},
{"date": "Jun 23", "Running": 124, "Cycling": 145},
{"date": "Jul 23", "Running": 164, "Cycling": 134},
{"date": "Aug 23", "Running": 123, "Cycling": 110},
{"date": "Sep 23", "Running": 132, "Cycling": 113},
{"date": "Oct 23", "Running": 124, "Cycling": 129},
{"date": "Nov 23", "Running": 149, "Cycling": 101},
{"date": "Dec 23", "Running": 129, "Cycling": 109},
]
_running_avg = round(sum(d["Running"] for d in data) / len(data))
_cycling_avg = round(sum(d["Cycling"] for d in data) / len(data))
_overall_avg = round((_running_avg + _cycling_avg) / 2)
ShowRunning = ClientStateVar.create("show_running", True)
ShowCycling = ClientStateVar.create("show_cycling", True)
AvgBPM = ClientStateVar.create("avg_bpm", _overall_avg)
def _legend_item(label: str, color: str, is_active: ClientStateVar) -> rx.Component:
return rx.el.button(
rx.el.div(
rx.el.span(class_name=f"size-3 rounded-[3px] {color}"),
rx.el.p(label, class_name="text-sm text-muted-foreground"),
class_name="flex items-center gap-1.5",
),
on_click=is_active.set_value(~is_active.value),
class_name=rx.cond(
is_active.value,
"text-left opacity-100 cursor-pointer",
"text-left opacity-40 cursor-pointer",
),
)
def _chart(show_y_axis: bool) -> rx.Component:
return rx.recharts.bar_chart(
chart_tooltip(),
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
interval="preserveStartEnd",
),
rx.recharts.y_axis(
width=30,
tick_line=False,
axis_line=False,
tick_size=15,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
hide=not show_y_axis,
),
rx.recharts.bar(
data_key="Running",
fill="var(--chart-2)",
is_animation_active=False,
class_name=rx.cond(ShowRunning.value, "opacity-100", "opacity-50").to(str),
),
rx.recharts.bar(
data_key="Cycling",
fill="var(--chart-3)",
is_animation_active=False,
class_name=rx.cond(ShowCycling.value, "opacity-100", "opacity-50").to(str),
),
data=data,
width="100%",
height=300,
)
def bar_chart_03():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Average BPM",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
rx.cond(
ShowRunning.value & ShowCycling.value,
str(_overall_avg),
rx.cond(
ShowRunning.value,
str(_running_avg),
rx.cond(
ShowCycling.value,
str(_cycling_avg),
"0",
),
),
),
"bpm",
class_name="text-3xl font-bold",
),
),
rx.el.ul(
_legend_item("Running", "bg-chart-2", ShowRunning),
_legend_item("Cycling", "bg-chart-3", ShowCycling),
class_name="flex gap-10 items-center justify-end",
),
rx.el.div(
rx.el.div(_chart(show_y_axis=False), class_name="sm:hidden"),
rx.el.div(_chart(show_y_axis=True), class_name="hidden sm:block"),
),
class_name=chart_tooltip_content([2, 3], "square")
+ " w-full flex flex-col gap-6",
)
Temperature
Zurich — 24 hour readout
import reflex as rx
from components.chart.chart_tooltip import chart_tooltip_content
data = [
{"hour": "00:00", "temperature": 12.8},
{"hour": "01:00", "temperature": 12.4},
{"hour": "02:00", "temperature": 12.2},
{"hour": "03:00", "temperature": 11.9},
{"hour": "04:00", "temperature": 11.7},
{"hour": "05:00", "temperature": 11.5},
{"hour": "06:00", "temperature": 11.3},
{"hour": "07:00", "temperature": 11.2},
{"hour": "08:00", "temperature": 11.5},
{"hour": "09:00", "temperature": 12.0},
{"hour": "10:00", "temperature": 13.0},
{"hour": "11:00", "temperature": 14.2},
{"hour": "12:00", "temperature": 15.5},
{"hour": "13:00", "temperature": 16.8},
{"hour": "14:00", "temperature": 17.5},
{"hour": "15:00", "temperature": 18.1},
{"hour": "16:00", "temperature": 18.2},
{"hour": "17:00", "temperature": 17.8},
{"hour": "18:00", "temperature": 17.2},
{"hour": "19:00", "temperature": 16.5},
{"hour": "20:00", "temperature": 15.8},
{"hour": "21:00", "temperature": 14.9},
{"hour": "22:00", "temperature": 14.2},
{"hour": "23:00", "temperature": 13.5},
]
def _chart(show_y_axis: bool) -> rx.Component:
return rx.recharts.bar_chart(
rx.recharts.cartesian_grid(
horizontal=True,
vertical=False,
stroke="color-mix(in oklab, var(--muted-foreground) 15%, transparent)",
),
rx.recharts.x_axis(
data_key="hour",
tick_line=False,
axis_line=False,
interval="preserveStartEnd",
height=50,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
label={
"value": "24H Temperature Readout (Zurich)",
"position": "insideBottom",
"style": {
"fill": "var(--muted-foreground)",
"fontSize": "12px",
},
},
),
rx.recharts.y_axis(
width=20,
tick_line=False,
axis_line=False,
tick={
"fill": "var(--foreground)",
"fontSize": 11,
},
hide=not show_y_axis,
),
rx.recharts.bar(
data_key="temperature",
fill="var(--chart-1)",
is_animation_active=False,
),
data=data,
width="100%",
height=320,
)
def bar_chart_04():
return rx.el.div(
rx.el.div(
rx.el.h3(
"Temperature",
class_name="text-base leading-snug font-medium",
),
rx.el.p(
"Zurich — 24 hour readout",
class_name="text-sm text-muted-foreground",
),
),
rx.el.div(
rx.el.div(_chart(show_y_axis=False), class_name="sm:hidden"),
rx.el.div(_chart(show_y_axis=True), class_name="hidden sm:block"),
),
class_name=chart_tooltip_content([1], "square") + " w-full flex flex-col gap-6",
)
KPI Cards
Compact metric summaries for dashboards.
Monthly active users+1.3%
996
Monthly sessions+9.1%
1,672
Monthly user growth-4.8%
5.1%
Monthly retention+2.7%
82.4%
import reflex as rx
from components.ui.card import card
data = [
{
"name": "Monthly active users",
"stat": "996",
"change": "+1.3%",
"color": "bg-chart-1",
},
{
"name": "Monthly sessions",
"stat": "1,672",
"change": "+9.1%",
"color": "bg-chart-2",
},
{
"name": "Monthly user growth",
"stat": "5.1%",
"change": "-4.8%",
"color": "bg-chart-3",
},
{
"name": "Monthly retention",
"stat": "82.4%",
"change": "+2.7%",
"color": "bg-chart-4",
},
]
def _kpi_card(name: str, stat: str, change: str, color: str) -> rx.Component:
is_positive = change.startswith("+")
return card.root(
rx.el.div(
rx.el.div(class_name=f"w-1 shrink-0 rounded {color}"),
rx.el.div(
rx.el.span(name, class_name="truncate text-sm text-muted-foreground"),
rx.el.span(
change,
class_name="text-sm font-medium "
+ ("text-emerald-600" if is_positive else "text-red-600"),
),
class_name="flex w-full items-center justify-between gap-3 truncate",
),
class_name="flex gap-3 px-4",
),
rx.el.div(
rx.el.p(stat, class_name="text-3xl font-semibold"),
class_name="mt-2 pl-4",
),
size="sm",
class_name="w-full border border-input/80 rounded-2xl !ring-0",
)
def kpi_card_01():
return rx.el.dl(
*[_kpi_card(**item) for item in data],
class_name="w-full grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-4",
)
Monthly users
42512-month avg
Monthly sessions
85812-month avg
Monthly churn
2.9%12-month avg
import reflex as rx
from components.chart.chart_tooltip import chart_tooltip, chart_tooltip_content
from components.ui.card import card
data = [
{"date": "Jan 23", "users": 234, "sessions": 1432, "churn": 5.2},
{"date": "Feb 23", "users": 431, "sessions": 1032, "churn": 4.3},
{"date": "Mar 23", "users": 543, "sessions": 1089, "churn": 5.1},
{"date": "Apr 23", "users": 489, "sessions": 988, "churn": 5.4},
{"date": "May 23", "users": 391, "sessions": 642, "churn": 5.5},
{"date": "Jun 23", "users": 582, "sessions": 786, "churn": 4.8},
{"date": "Jul 23", "users": 482, "sessions": 673, "churn": 4.5},
{"date": "Aug 23", "users": 389, "sessions": 761, "churn": 0},
{"date": "Sep 23", "users": 521, "sessions": 793, "churn": 0},
{"date": "Oct 23", "users": 434, "sessions": 543, "churn": 0},
{"date": "Nov 23", "users": 332, "sessions": 678, "churn": 0},
{"date": "Dec 23", "users": 275, "sessions": 873, "churn": 0},
]
categories = [
{
"name": "Monthly users",
"key": "users",
"color": 1,
"avg": round(sum(d["users"] for d in data) / len(data)),
"suffix": "",
},
{
"name": "Monthly sessions",
"key": "sessions",
"color": 2,
"avg": round(sum(d["sessions"] for d in data) / len(data)),
"suffix": "",
},
{
"name": "Monthly churn",
"key": "churn",
"color": 3,
"avg": round(sum(d["churn"] for d in data) / len(data), 1),
"suffix": "%",
},
]
def _gradient(id_: str, color: int) -> rx.Component:
return rx.el.svg.linear_gradient(
rx.el.svg.stop(
stop_color=f"var(--chart-{color})", offset="5%", stop_opacity=0.2
),
rx.el.svg.stop(
stop_color=f"var(--chart-{color})", offset="95%", stop_opacity=0.2
),
x1=0,
x2=0,
y1=0,
y2=1,
id=id_,
)
def _mini_chart(key: str, color: int) -> rx.Component:
return rx.recharts.area_chart(
chart_tooltip(label="hide"),
rx.el.svg.defs(_gradient(key, color)),
rx.recharts.x_axis(
data_key="date",
tick_line=False,
axis_line=False,
interval="preserveStartEnd",
tick={
"fill": "var(--foreground)",
"fontSize": 10,
},
),
rx.recharts.y_axis(hide=True),
rx.recharts.area(
data_key=key,
fill=f"url(#{key})",
stroke=f"var(--chart-{color})",
stroke_width=2,
dot=False,
is_animation_active=False,
active_dot={"fill": "var(--chart-1)", "stroke": f"var(--chart-{color})"},
),
data=data,
width="100%",
height=100,
)
def _kpi_mini_chart(
name: str, key: str, color: int, avg: float, suffix: str
) -> rx.Component:
return card.root(
rx.el.div(
rx.el.p(name, class_name="text-sm text-muted-foreground"),
rx.el.div(
rx.el.span(
f"{avg:,}{suffix}",
class_name="text-xl font-semibold",
),
rx.el.span(
"12-month avg",
class_name="text-sm text-muted-foreground",
),
class_name="mt-1 flex items-baseline justify-between gap-2",
),
_mini_chart(key, color),
),
size="sm",
class_name=chart_tooltip_content([color], "square")
+ " w-full px-4 pt-4 pb-0 border border-input/80 rounded-2xl !ring-0",
)
def kpi_card_02():
return rx.el.dl(
*[_kpi_mini_chart(**item) for item in categories],
class_name="w-full grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3",
)