charts
Registry

Activity Chart

Compact rounded-bar metric card with a Polar title, headline value, tooltip, and hover dimming.

Preview

Preview

Sessions

64.4k

Variants

Loading
Loading

Installation

Install with the shadcn CLI. This copies source into your project.

bash
npx shadcn@latest add https://livedocs.xyz/r/activity-chart.json
Namespace (after one-time setup)
npx shadcn@latest add @livedocs/activity-chart

Usage

tsx
import { ActivityChart } from "@/components/ui/activity-chart"

export function Example() {
  return (
    <ActivityChart
      title="Sessions"
      value="48.2k"
     
      data={[
        { label: "D3", value: 1240 },
        { label: "D9", value: 2140 },
        { label: "D15", value: 2420 },
        { label: "D21", value: 2660 },
        { label: "D27", value: 2890 },
      ]}
    />
  )
}

Props

PropTypeDefaultDescription
titlestringCard heading
valuestringHeadline value shown under the title
dataActivityDatum[]Bars as { label, value } pairs
configChartConfigSeries labels and colors
isLoadingbooleanShow shared loading feedback instead of bars
reactionChartReactionOptionsOptional explicit emotion or metric comparison
onBarClick(datum: ActivityDatum, index: number) => voidOptional bar activation handler

Source

"use client";

import * as React from "react";
import {
  Bar,
  BarChart as RechartsBarChart,
  Cell,
  ResponsiveContainer,
  XAxis,
} from "recharts";

import {
  ChartContainer,
  ChartHeading,
  ChartTooltip,
  colorVar,
  type ChartConfig,
} from "@/components/ui/chart";
import { Card } from "@/components/ui/card";
import { type ChartReactionOptions } from "@/components/ui/chart-reactions";
import { cn } from "@/lib/utils";

export type ActivityDatum = { label: string; value: number };

const defaultConfig = { value: { label: "Value", color: "var(--chart-2)" } } satisfies ChartConfig;

function ActivityChartRoot({
  title,
  value,
  data,
  config = defaultConfig,
  className,
  isLoading,
  reaction,
  onBarClick,
}: {
  title: string;
  value: string;
  data: ActivityDatum[];
  config?: ChartConfig;
  className?: string;
  isLoading?: boolean;
  reaction?: ChartReactionOptions;
  onBarClick?: (datum: ActivityDatum, index: number) => void;
}) {
  const [hovered, setHovered] = React.useState<number>();
  const [focused, setFocused] = React.useState<number>();
  const active = hovered ?? focused;

  return (
    <Card className={cn("p-5 sm:p-6", className)}>
      <ChartContainer
        isLoading={isLoading}
        loadingVariant="bar"
        reaction={reaction}
        config={{ ...defaultConfig, ...config }}
        data={data}
        variant="plain"
        className="w-full justify-start"
      >
        <ChartHeading title={title} value={value} />
        <div className="mt-3 h-44 w-full">
          <ResponsiveContainer width="100%" height="100%">
            <RechartsBarChart data={data} barCategoryGap="28%" margin={{ top: 4, right: 0, left: 0, bottom: 0 }}>
              <ChartTooltip />
              <XAxis
                dataKey="label"
                tickLine={false}
                axisLine={false}
                interval="preserveStartEnd"
                minTickGap={44}
                tick={{ fill: "var(--muted-foreground)", fontSize: 11 }}
              />
              <Bar dataKey="value" radius={[4, 4, 4, 4]} maxBarSize={10} isAnimationActive={false}>
                {data.map((datum, index) => (
                  <Cell
                    key={datum.label}
                    fill={colorVar("value")}
                    opacity={active !== undefined && active !== index ? 0.6 : 1}
                    tabIndex={0}
                    role={onBarClick ? "button" : "img"}
                    aria-label={`${datum.label}: ${datum.value.toLocaleString("en-US")}`}
                    className="cursor-pointer transition-opacity duration-150 motion-reduce:transition-none focus-visible:outline-none"
                    onMouseEnter={() => setHovered(index)}
                    onMouseLeave={() => setHovered(undefined)}
                    onFocus={() => setFocused(index)}
                    onBlur={() => setFocused(undefined)}
                    onClick={() => onBarClick?.(datum, index)}
                    onKeyDown={(event) => {
                      if (onBarClick && (event.key === "Enter" || event.key === " ")) {
                        event.preventDefault();
                        onBarClick(datum, index);
                      }
                    }}
                  />
                ))}
              </Bar>
            </RechartsBarChart>
          </ResponsiveContainer>
        </div>
      </ChartContainer>
    </Card>
  );
}

export const ActivityChart = Object.assign(ActivityChartRoot, {});