Dark

GitHub

Line Chart

Showing total visitors for the last 6 months

Trending up by 5.2% this month

January - June 2024

Line Chart - Linear

Showing total visitors for the last 6 months

Trending up by 5.2% this month

January - June 2024

Line Chart - Label

Showing total visitors for the last 6 months

Trending up by 5.2% this month

January - June 2024

Line Chart - Multiple

Showing total visitors for the last 6 months

Trending up by 5.2% this month

January - June 2024

Line Chart - Label

Showing total visitors for the last 6 months

Trending up by 5.2% this month

January - June 2024

Line Chart - Minimal

Showing total visitors for the last 6 months

Trending up by 5.2% this month

January - June 2024

Line Chart - Dynamic

Showing total visitors for the last 6 months

Trending up by 5.2% this month

January - June 2024

Desktop

Mobile

import reflex as rx
from typing import Any, Dict, List, Optional, Union


class LineChart:
    def __init__(self, data: List[Dict[str, Any]]):
        self._data = data
        self._x_key: Optional[str] = None
        self._show_y_axis = False
        self._y_axis_props: Dict[str, Any] = {}
        self._show_x_axis: bool = True
        self._x_axis_props: Dict[str, Any] = {}
        self._series: List[Dict[str, Any]] = []
        self._show_tooltip = True
        self._show_grid = True
        self._width: Union[int, str] = "100%"
        self._height: int = 250
        self._extra: Dict[str, Any] = {}
        self._components: List[Any] = []
        self._custom_legend: Optional[Dict[str, str]] = None
        self._legend_position: str = "bottom"

    def x(self, key: str) -> "LineChart":
        self._x_key = key
        return self

    def series(
        self,
        key: str,
        color: str = "chart-1",
        stroke: Optional[str] = None,
        type_: Optional[str] = None,
        stack_id: Optional[str] = None,
        x_axis_id: Optional[Union[str, int]] = None,
        y_axis_id: Optional[Union[str, int]] = None,
        dot: Optional[bool] = None,
        active_dot: Optional[bool] = None,
        stroke_width: Optional[int] = None,
        connect_nulls: Optional[bool] = None,
        is_animation_active: Optional[bool] = None,
        on_click: Optional[Any] = None,
        **kwargs: Any,
    ) -> "LineChart":
        # Use rx.cond to handle color variable logic reactively
        color_var = rx.cond(color.startswith("var("), color, f"var(--{color})")

        # Handle stroke logic reactively
        stroke_final = (
            rx.cond(
                stroke is None,
                color_var,
                rx.cond(stroke.startswith("var("), stroke, f"var(--{stroke})"),
            )
            if stroke is not None
            else color_var
        )

        line_props = {
            "data_key": key,
            "stroke": stroke_final,
            "stroke_width": stroke_width or 1,
            "type_": type_,
            "stack_id": stack_id,
            "x_axis_id": x_axis_id,
            "y_axis_id": y_axis_id,
            "dot": dot,
            "active_dot": active_dot,
            "connect_nulls": connect_nulls,
            "is_animation_active": rx.cond(
                is_animation_active is None, True, is_animation_active
            )
            if is_animation_active is not None
            else True,
            "on_click": on_click,
        }

        # Store the series config - we'll filter None values in __call__
        self._series.append(
            {
                "config": line_props,
                "kwargs": kwargs,
                "key": key,
            }
        )
        return self

    def x_axis(
        self,
        show: bool = True,
        type_: Optional[str] = None,
        data_key: Optional[str] = None,
        hide: bool = False,
        axis_line: bool = False,
        tick_size: Optional[int] = 10,
        tick_line: bool = False,
        custom_attrs: Optional[Dict[str, Any]] = None,
        interval: Optional[str] = "preserveStartEnd",
        **kwargs: Any,
    ) -> "LineChart":
        self._show_x_axis = show
        if show:
            props = {
                "data_key": data_key,
                "hide": hide,
                "type_": type_,
                "axis_line": axis_line,
                "tick_size": tick_size,
                "tick_line": tick_line,
                "custom_attrs": custom_attrs or {"fontSize": "12px"},
                "interval": interval,
            }
            props.update(kwargs)
            self._x_axis_props = props
        return self

    def y_axis(
        self,
        show: bool = True,
        axis_line: bool = False,
        min_tick_gap: Optional[int] = 50,
        tick_size: Optional[int] = 10,
        tick_line: bool = False,
        custom_attrs: Optional[Dict[str, Any]] = None,
        **kwargs: Any,
    ) -> "LineChart":
        self._show_y_axis = show
        if show:
            props = {
                "axis_line": axis_line,
                "min_tick_gap": min_tick_gap,
                "tick_size": tick_size,
                "tick_line": tick_line,
                "custom_attrs": custom_attrs or {"fontSize": "12px"},
            }
            props.update(kwargs)
            self._y_axis_props = props
        return self

    def tooltip(self, show: bool = True) -> "LineChart":
        self._show_tooltip = show
        return self

    def grid(self, show: bool = True) -> "LineChart":
        self._show_grid = show
        return self

    def size(self, width: Union[int, str], height: int) -> "LineChart":
        self._width = width
        self._height = height
        return self

    def config(
        self,
        sync_id: Optional[str] = None,
        layout: Optional[str] = None,
        margin: Optional[Dict[str, int]] = None,
        stack_offset: Optional[str] = None,
        sync_method: Optional[str] = None,
        reverse_stack_order: Optional[bool] = None,
        **kwargs: Any,
    ) -> "LineChart":
        if sync_id is not None:
            self._extra["sync_id"] = sync_id
        if layout is not None:
            self._extra["layout"] = layout
        if margin is not None:
            self._extra["margin"] = margin
        if stack_offset is not None:
            self._extra["stack_offset"] = stack_offset
        if sync_method is not None:
            self._extra["sync_method"] = sync_method
        if reverse_stack_order is not None:
            self._extra["reverse_stack_order"] = reverse_stack_order

        self._extra.update(kwargs)
        return self

    def legend(self, labels: Dict[str, str], position: str = "bottom") -> "LineChart":
        self._custom_legend = labels
        self._legend_position = position
        return self

    def add(self, *components: Any) -> "LineChart":
        self._components.extend(components)
        return self

    def _create_series_component(self, series_config: Dict[str, Any]) -> rx.Component:
        """Create a single series component with proper None filtering"""
        config = series_config["config"]
        kwargs = series_config["kwargs"]

        # Filter out None values reactively
        filtered_props = {}
        for key, value in config.items():
            if value is not None:
                filtered_props[key] = value

        # Add kwargs
        filtered_props.update(kwargs)

        # Handle labels if present
        label_list = []
        if filtered_props.get("label"):
            if isinstance(filtered_props["label"], dict):
                label_list.append(rx.recharts.label_list(**filtered_props["label"]))
            elif filtered_props["label"] is True:
                label_list.append(rx.recharts.label_list())
            # Remove label from props since it's handled separately
            filtered_props.pop("label", None)

        return rx.recharts.line(*label_list, **filtered_props)

    def __call__(self, **kwargs: Any) -> rx.Component:
        if not self._x_key:
            raise ValueError("X axis key must be set with `.x()` before rendering.")

        # Build components list
        components = []

        # Add custom components first
        components.extend(self._components)

        # Add tooltip conditionally
        if self._show_tooltip:
            components.append(
                rx.recharts.graphing_tooltip(
                    is_animation_active=False,
                    separator="",
                    cursor=False,
                    item_style={
                        "color": "currentColor",
                        "display": "flex",
                        "paddingBottom": "0px",
                        "justifyContent": "space-between",
                        "textTransform": "capitalize",
                    },
                    label_style={"color": rx.color("slate", 10), "fontWeight": "500"},
                    content_style={
                        "background": rx.color_mode_cond(
                            "oklch(0.97 0.00 0)", "oklch(0.14 0.00 286)"
                        ),
                        "borderColor": rx.color("slate", 5),
                        "borderRadius": "5px",
                        "fontFamily": "IBM Plex Mono,ui-monospace,monospace",
                        "fontSize": "0.875rem",
                        "lineHeight": "1.25rem",
                        "fontWeight": "500",
                        "letterSpacing": "-0.01rem",
                        "minWidth": "8rem",
                        "width": "175px",
                        "padding": "0.375rem 0.625rem",
                        "position": "relative",
                    },
                )
            )

        # Add grid conditionally
        if self._show_grid:
            components.append(
                rx.recharts.cartesian_grid(
                    horizontal=True, vertical=False, class_name="opacity-25"
                )
            )

        # Add Y axis conditionally
        if self._show_y_axis:
            components.append(rx.recharts.y_axis(**self._y_axis_props))

        # Add series components
        for series_config in self._series:
            components.append(self._create_series_component(series_config))

        # Add X axis conditionally
        if self._show_x_axis:
            x_axis_props = dict(self._x_axis_props)
            x_axis_props.setdefault("data_key", self._x_key)
            x_axis_props.setdefault("axis_line", False)
            x_axis_props.setdefault("tick_size", 10)
            x_axis_props.setdefault("tick_line", False)
            x_axis_props.setdefault("interval", "preserveStartEnd")
            x_axis_props.setdefault("custom_attrs", {"fontSize": "12px"})
            components.append(rx.recharts.x_axis(**x_axis_props))

        # Create the main chart
        chart = rx.recharts.line_chart(
            *components,
            data=self._data,
            width=self._width,
            height=self._height,
            **self._extra,
            **kwargs,
        )

        # Handle custom legend
        if self._custom_legend:
            legend_items = []
            for series_config in self._series:
                key = series_config["key"]
                stroke = series_config["config"]["stroke"]
                label = self._custom_legend.get(key, key)

                legend_items.append(
                    rx.hstack(
                        rx.box(bg=stroke, class_name="w-3 h-3 rounded-sm"),
                        rx.text(
                            label,
                            class_name="text-sm font-semibold",
                            color=rx.color("slate", 11),
                        ),
                        spacing="2",
                        align="center",
                    )
                )

            legend = rx.hstack(
                *legend_items,
                class_name="justify-center gap-4 py-2",
            )

            return rx.box(
                *(
                    [legend, chart]
                    if self._legend_position == "top"
                    else [chart, legend]
                ),
                class_name="w-full flex flex-col",
            )

        return chart

@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
    --chart-1: oklch(0.81 0.1 252);
    --chart-2: oklch(0.62 0.19 260);
    --chart-3: oklch(0.55 0.22 263);
    --chart-4: oklch(0.49 0.22 264);
    --chart-5: oklch(0.42 0.18 266);
}

.dark {
    --chart-1: oklch(0.81 0.1 252);
    --chart-2: oklch(0.62 0.19 260);
    --chart-3: oklch(0.55 0.22 263);
    --chart-4: oklch(0.49 0.22 264);
    --chart-5: oklch(0.42 0.18 266);
}

LineChart

Instantiate the LineChart with data.

ParameterTypeRequiredDefaultDescription

data

List[Dict[str, Any]]

True

The list of data points to be plotted.

x

Set the XAxis data key.

ParameterTypeRequiredDefaultDescription

key

str

True

The key in data to use for the X axis.

series

Add a data series to the line chart.

ParameterTypeRequiredDefaultDescription

key

str

True

Data key for this series.

stroke

str

False

'var(--chart-1)'

Stroke color.

stroke_width

int

False

2

Line thickness.

type_

str

False

'natural'

Line type (e.g., 'monotone', 'linear').

dot

Union[bool, str]

False

False

Show dots on data points.

is_animation_active

bool

False

True

Enable line animation.

stack_id

Optional[str]

False

None

ID for stacking lines.

name

Optional[str]

False

None

Custom legend name.

legend_type

Optional[str]

False

None

Legend symbol shape.

label

Union[bool, Dict[str, Any]]

False

None

Show value labels.

tooltip

Toggle tooltip visibility.

ParameterTypeRequiredDefaultDescription

show

bool

False

True

Show tooltip if True.

grid

Toggle grid visibility.

ParameterTypeRequiredDefaultDescription

show

bool

False

True

Show grid if True.

size

Set chart size.

ParameterTypeRequiredDefaultDescription

width

Union[int, str]

True

'100%'

Chart width.

height

int

True

250

Chart height in pixels.

config

Additional config for the line chart.

ParameterTypeRequiredDefaultDescription

sync_id

Optional[str]

False

None

Sync charts by ID.

layout

Optional[str]

False

None

Layout direction (horizontal or vertical).

margin

Optional[Dict[str, int]]

False

None

Outer chart margins.

sync_method

Optional[str]

False

None

Sync method, e.g., 'index'.

add

Add extra recharts components manually.

ParameterTypeRequiredDefaultDescription

components

Any

False

None

Extra Recharts elements.

legend

Configure custom legend labels and position.

ParameterTypeRequiredDefaultDescription

labels

Dict[str, str]

True

Series key to legend label mapping.

position

str

False

'bottom'

Legend position ('top' or 'bottom').

y_axis

Enable or disable Y axis with optional style overrides.

ParameterTypeRequiredDefaultDescription

show

bool

False

False

Show Y axis.

type_

Optional[str]

False

None

Axis scale type.

data_key

Optional[str]

False

None

Data key for Y axis.

axis_line

bool

False

False

Show axis line.

min_tick_gap

int

False

5

Min gap between ticks.

tick_size

int

False

10

Tick size.

tick_line

bool

False

False

Show tick lines.

custom_attrs

Dict[str, Any]

False

None

Extra attributes.

x_axis

Enable or disable X axis with optional style overrides.

ParameterTypeRequiredDefaultDescription

show

bool

False

True

Show X axis.

type_

Optional[str]

False

None

Axis scale type.

data_key

Optional[str]

False

None

Data key for X axis.

axis_line

bool

False

False

Show axis line.

tick_size

int

False

10

Tick size.

tick_line

bool

False

False

Show tick lines.

custom_attrs

Dict[str, Any]

False

{'fontSize': '12px'}

Extra X axis attributes.

interval

Optional[str]

False

'preserveStartEnd'

Tick interval policy.

Home