ComponentsBlocksDocsExamples
Cards
Statistic CardsNewList CardsSoonTable CardsSoonTimeline CardsSoonForm CardsSoon
Charts
Line ChartsNewArea ChartsNewBar ChartsSoonPie ChartsSoonDoughnut ChartsSoonRadar ChartsSoon
Navigation
DropdownsSoonNavbarsSoonTabsSoonBreadcrumbsSoonVertical NavigationSoon
Lists
Stacked ListsSoonTablesSoonData GridsSoonTreesSoonFeedsSoon
Forms
Form LayoutsSoonForm WizardsSoonForm UploadsSoonAction FormsSoonModal FormsSoonDrawer FormsSoon
Feedback
AlertsSoonDialogsSoonNotificationsSoonEmpty StatesSoonLoading StatesSoonOverlay ContentSoon
Marketing
HeroNewPricing TablesSoonFeaturesSoonCall to ActionSoonTestimonialsSoon
Ecommerce
HeroNewPagesNewCartsNewPricing TablesSoonFeaturesSoonCall to ActionSoonTestimonialsSoon
Application
App-ShellNew
  1. Blocks
  2. Charts

Charts

Line Charts

9 free modern line charts designed to present key metrics and insights. Each chart features unique layouts, data visualizations, and styling options. Perfect for dashboards, admin panels, and analytics pages.

View all →
line-chart-1
Open in
Loading...
charts/line-charts/line-chart-1.tsx
'use client';

import React from 'react';
import { Badge } from '@/registry/default/ui/badge';
import { Button } from '@/registry/default/ui/button';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from '@/registry/default/ui/dropdown-menu';
import { ArrowDown, ArrowUp, Calendar, Download, Filter, MoreHorizontal, RefreshCw, Share2 } from 'lucide-react';
import { Area, CartesianGrid, ComposedChart, Line, ReferenceLine, XAxis, YAxis } from 'recharts';

// Sales data for B2B Software Company (6 months)
const salesData = [
  { month: 'Jan 24', goals: 250000, sales: 280000, salesArea: 280000 },
  { month: 'Feb 24', goals: 420000, sales: 350000, salesArea: 350000 },
  { month: 'Mar 24', goals: 380000, sales: 480000, salesArea: 480000 },
  { month: 'Apr 24', goals: 520000, sales: 390000, salesArea: 390000 },
  { month: 'May 24', goals: 300000, sales: 520000, salesArea: 520000 },
  { month: 'Jun 24', goals: 550000, sales: 465000, salesArea: 465000 },
];

// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
  goals: {
    label: 'Goals',
    color: 'var(--color-pink-500)',
  },
  sales: {
    label: 'Sales',
    color: 'var(--color-teal-500)',
  },
} satisfies ChartConfig;

// Custom Tooltip
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    dataKey: string;
    value: number;
    color: string;
  }>;
  label?: string;
}

const ChartLabel = ({ label, color = chartConfig.sales.color }: { label: string; color: string }) => {
  return (
    <div className="flex items-center gap-1.5">
      <div className="size-3.5 border-4 rounded-full bg-background" style={{ borderColor: color }}></div>
      <span className="text-muted-foreground">{label}</span>
    </div>
  );
};

const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
  if (active && payload && payload.length) {
    // Filter out salesArea from tooltip
    const filteredPayload = payload.filter((entry) => entry.dataKey !== 'salesArea');

    return (
      <div className="rounded-lg border bg-popover p-3 shadow-sm shadow-black/5 min-w-[180px]">
        <div className="text-xs font-medium text-muted-foreground tracking-wide mb-2.5">{label}</div>
        <div className="space-y-2">
          {filteredPayload.map((entry, index) => {
            const config = chartConfig[entry.dataKey as keyof typeof chartConfig];
            return (
              <div key={index} className="flex items-center gap-2 text-xs">
                <ChartLabel label={config?.label + ':'} color={entry.color} />
                <span className="font-semibold text-popover-foreground">${(entry.value / 1000000).toFixed(1)}M</span>
                {config?.label == 'Goals' && (
                  <Badge
                    variant={
                      ((entry.value - filteredPayload[0].value) / filteredPayload[0].value) * 100 > 0
                        ? 'success'
                        : 'destructive'
                    }
                    appearance="light"
                    className="text-xs flex items-center gap-1"
                  >
                    {((entry.value - filteredPayload[0].value) / filteredPayload[0].value) * 100 > 0 ? (
                      <ArrowUp className="size-3" />
                    ) : (
                      <ArrowDown className="size-3" />
                    )}
                    {Math.abs(((entry.value - filteredPayload[0].value) / filteredPayload[0].value) * 100).toFixed(0)}%
                  </Badge>
                )}
              </div>
            );
          })}
        </div>
      </div>
    );
  }
  return null;
};

export default function LineChart1() {
  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="w-full lg:max-w-4xl">
        <CardHeader className="border-0 min-h-auto pt-6 pb-6">
          <CardTitle className="text-base font-semibold">Sales Overview</CardTitle>
          <CardToolbar>
            <div className="flex items-center gap-4 text-sm">
              <ChartLabel label="Sales" color={chartConfig.sales.color} />
              <ChartLabel label="Goals" color={chartConfig.goals.color} />
            </div>
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <Button variant="outline" size="icon-sm" className="-me-1.5">
                  <MoreHorizontal className="size-4" />
                </Button>
              </DropdownMenuTrigger>
              <DropdownMenuContent align="end" side="bottom">
                <DropdownMenuItem>
                  <Download className="size-4" />
                  Export Data
                </DropdownMenuItem>
                <DropdownMenuItem>
                  <Calendar className="size-4" />
                  Change Period
                </DropdownMenuItem>
                <DropdownMenuItem>
                  <Filter className="size-4" />
                  Filter Data
                </DropdownMenuItem>
                <DropdownMenuItem>
                  <RefreshCw className="size-4" />
                  Refresh
                </DropdownMenuItem>
                <DropdownMenuSeparator />
                <DropdownMenuItem>
                  <Share2 className="size-4" />
                  Share Report
                </DropdownMenuItem>
              </DropdownMenuContent>
            </DropdownMenu>
          </CardToolbar>
        </CardHeader>

        <CardContent className="px-2.5 flex flex-col items-end">
          <ChartContainer
            config={chartConfig}
            className="h-[350px] w-full [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
          >
            <ComposedChart
              data={salesData}
              margin={{
                top: 5,
                right: 15,
                left: 5,
                bottom: 5,
              }}
            >
              <defs>
                <linearGradient id="salesGradient" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="0%" stopColor={chartConfig.sales.color} stopOpacity={0.3} />
                  <stop offset="100%" stopColor={chartConfig.sales.color} stopOpacity={0.05} />
                </linearGradient>
              </defs>

              <CartesianGrid
                strokeDasharray="4 4"
                stroke="var(--input)"
                strokeOpacity={1}
                horizontal={true}
                vertical={false}
              />

              <XAxis
                dataKey="month"
                axisLine={false}
                tickLine={false}
                tick={{ fontSize: 11, className: 'text-muted-foreground' }}
                dy={5}
                tickMargin={12}
              />

              <YAxis
                axisLine={false}
                tickLine={false}
                tick={{ fontSize: 11, className: 'text-muted-foreground' }}
                tickFormatter={(value) => `$${(value / 1000000).toFixed(1)}M`}
                domain={['dataMin - 50000', 'dataMax + 50000']}
                tickMargin={12}
              />

              {/* Current month reference line */}
              <ReferenceLine x="Mar 24" stroke={chartConfig.sales.color} strokeWidth={1} />

              {/* Tooltip */}
              <ChartTooltip
                content={<CustomTooltip />}
                cursor={{
                  stroke: 'var(--input)',
                  strokeWidth: 1,
                  strokeDasharray: 'none',
                }}
              />

              {/* Sales area with gradient background */}
              <Area
                type="linear"
                dataKey="salesArea"
                stroke="transparent"
                fill="url(#salesGradient)"
                strokeWidth={0}
                dot={false}
              />

              {/* Sales line with dots */}
              <Line
                type="linear"
                dataKey="sales"
                stroke={chartConfig.sales.color}
                strokeWidth={2}
                dot={{
                  fill: 'var(--background)',
                  strokeWidth: 2,
                  r: 6,
                  stroke: chartConfig.sales.color,
                }}
              />

              {/* Goals line (dashed) */}
              <Line
                type="linear"
                dataKey="goals"
                stroke={chartConfig.goals.color}
                strokeWidth={2}
                strokeDasharray="4 4"
                dot={{
                  fill: 'var(--background)',
                  strokeWidth: 2,
                  r: 6,
                  stroke: chartConfig.goals.color,
                  strokeDasharray: '0',
                }}
              />
            </ComposedChart>
          </ChartContainer>
        </CardContent>
      </Card>
    </div>
  );
}
line-chart-2
Open in
Loading...
charts/line-charts/line-chart-2.tsx
'use client';

import React, { useState } from 'react';
import { Badge } from '@/registry/default/ui/badge';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { Select, SelectContent, SelectItem, SelectTrigger } from '@/registry/default/ui/select';
import { TrendingUp } from 'lucide-react';
import { Area, CartesianGrid, ComposedChart, Line, XAxis, YAxis } from 'recharts';

// Cashflow data for 12 months
const cashflowData = [
  { month: 'JAN', value: 2100 },
  { month: 'FEB', value: 2300 },
  { month: 'MAR', value: 1900 },
  { month: 'APR', value: 4800 },
  { month: 'MAY', value: 5200 },
  { month: 'JUN', value: 8900 },
  { month: 'JUL', value: 6200 },
  { month: 'AUG', value: 7100 },
  { month: 'SEP', value: 9400 },
  { month: 'OCT', value: 10200 },
  { month: 'NOV', value: 11100 },
  { month: 'DEC', value: 11800 },
];

// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
  value: {
    label: 'Cashflow',
    color: 'var(--color-violet-500)',
  },
} satisfies ChartConfig;

// Custom Tooltip
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    dataKey: string;
    value: number;
    color: string;
  }>;
  label?: string;
}

const CustomTooltip = ({ active, payload }: TooltipProps) => {
  if (active && payload && payload.length) {
    return (
      <>
        <div className="rounded-lg bg-zinc-900 text-white p-3 shadow-lg">
          <div className="text-xs font-medium mb-1">Total:</div>
          <div className="text-sm font-semibold">${payload[0].value.toLocaleString()}</div>
        </div>
      </>
    );
  }
  return null;
};

// Period configuration
const PERIODS = {
  '6m': {
    key: '6m',
    label: '6 months',
    dateRange: 'Jul 01 - Dec 31, 2024',
  },
  '12m': {
    key: '12m',
    label: '12 months',
    dateRange: 'Jan 01 - Dec 31, 2024',
  },
  '2y': {
    key: '2y',
    label: '2 years',
    dateRange: 'Jan 01, 2023 - Dec 31, 2024',
  },
} as const;

type PeriodKey = keyof typeof PERIODS;

export default function LineChart2() {
  const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('12m');

  // Filter data based on selected period
  const getFilteredData = () => {
    switch (selectedPeriod) {
      case '6m':
        return cashflowData.slice(-6);
      case '12m':
        return cashflowData;
      case '2y':
        // Simulate 2 years data by duplicating and modifying the current year
        const previousYear = cashflowData.map((item) => ({
          month: `${item.month} '23`,
          value: Math.round(item.value * 0.85), // 15% lower for previous year
        }));
        const currentYear = cashflowData.map((item) => ({
          month: `${item.month} '24`,
          value: item.value,
        }));
        return [...previousYear, ...currentYear];
      default:
        return cashflowData;
    }
  };

  const filteredData = getFilteredData();

  // Get current period configuration
  const currentPeriod = PERIODS[selectedPeriod];

  // Calculate total and percentage based on filtered data
  const totalCash = filteredData.reduce((sum, item) => sum + item.value, 0);
  const lastValue = filteredData[filteredData.length - 1]?.value || 0;
  const previousValue = filteredData[filteredData.length - 2]?.value || 0;
  const percentageChange = previousValue > 0 ? ((lastValue - previousValue) / previousValue) * 100 : 0;

  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="w-full lg:max-w-4xl ">
        <CardHeader className="border-0 min-h-auto pt-6 pb-4">
          <CardTitle className="text-lg font-semibold">Cashflow</CardTitle>
          <CardToolbar>
            <Select value={selectedPeriod} onValueChange={(value) => setSelectedPeriod(value as PeriodKey)}>
              <SelectTrigger>{currentPeriod.label}</SelectTrigger>
              <SelectContent align="end">
                {Object.values(PERIODS).map((period) => (
                  <SelectItem key={period.key} value={period.key}>
                    {period.label}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </CardToolbar>
        </CardHeader>

        <CardContent className="px-0">
          {/* Stats Section */}
          <div className="px-5 mb-8">
            <div className="text-xs font-medium text-muted-foreground tracking-wide mb-2">
              {currentPeriod.dateRange}
            </div>
            <div className="flex items-center gap-3 mb-4">
              <div className="text-3xl font-bold">${totalCash.toLocaleString()}</div>
              <Badge variant="success" appearance="light">
                <TrendingUp className="size-3" />
                {Math.abs(percentageChange).toFixed(2)}%
              </Badge>
            </div>
          </div>

          {/* Chart */}
          <div className="relative">
            <ChartContainer
              config={chartConfig}
              className="h-[300px] w-full ps-1.5 pe-2.5 overflow-visible [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
            >
              <ComposedChart
                data={filteredData}
                margin={{
                  top: 25,
                  right: 25,
                  left: 0,
                  bottom: 25,
                }}
                style={{ overflow: 'visible' }}
              >
                {/* Gradient */}
                <defs>
                  <linearGradient id="cashflowGradient" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stopColor={chartConfig.value.color} stopOpacity={0.15} />
                    <stop offset="100%" stopColor={chartConfig.value.color} stopOpacity={0} />
                  </linearGradient>
                  <filter id="dotShadow" x="-50%" y="-50%" width="200%" height="200%">
                    <feDropShadow dx="2" dy="2" stdDeviation="3" floodColor="rgba(0,0,0,0.5)" />
                  </filter>
                </defs>

                <CartesianGrid
                  strokeDasharray="4 12"
                  stroke="var(--input)"
                  strokeOpacity={1}
                  horizontal={true}
                  vertical={false}
                />

                <XAxis
                  dataKey="month"
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 12 }}
                  tickMargin={12}
                  dy={10}
                />

                <YAxis
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 12 }}
                  tickFormatter={(value) => `${value / 1000}K`}
                  domain={[0, 'dataMax + 1000']}
                  tickCount={6}
                  tickMargin={12}
                />

                <ChartTooltip
                  content={<CustomTooltip />}
                  cursor={{
                    stroke: chartConfig.value.color,
                    strokeWidth: 1,
                    strokeDasharray: 'none',
                  }}
                />

                {/* Gradient area */}
                <Area
                  type="linear"
                  dataKey="value"
                  stroke="transparent"
                  fill="url(#cashflowGradient)"
                  strokeWidth={0}
                  dot={false}
                />

                {/* Main cashflow line */}
                <Line
                  type="linear"
                  dataKey="value"
                  stroke={chartConfig.value.color}
                  strokeWidth={3}
                  dot={(props) => {
                    const { cx, cy, payload } = props;
                    if (payload.month === 'JUN' || payload.month === 'NOV') {
                      return (
                        <circle
                          key={`dot-${cx}-${cy}`}
                          cx={cx}
                          cy={cy}
                          r={6}
                          fill={chartConfig.value.color}
                          stroke="white"
                          strokeWidth={2}
                          filter="url(#dotShadow)"
                        />
                      );
                    }
                    return <g key={`dot-${cx}-${cy}`} />; // Return empty group for other points
                  }}
                  activeDot={{
                    r: 6,
                    fill: chartConfig.value.color,
                    stroke: 'white',
                    strokeWidth: 2,
                    filter: 'url(#dotShadow)',
                  }}
                />
              </ComposedChart>
            </ChartContainer>
          </div>
        </CardContent>
      </Card>
    </div>
  );
}
line-chart-3
Open in
Loading...
charts/line-charts/line-chart-3.tsx
'use client';

import React, { useState } from 'react';
import { Button } from '@/registry/default/ui/button';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from '@/registry/default/ui/chart';
import { ToggleGroup, ToggleGroupItem } from '@/registry/default/ui/toggle-group';
import { ChartNoAxesCombined, Info } from 'lucide-react';
import { CartesianGrid, Line, LineChart } from 'recharts';

// Revenue performance data for different periods (in thousands)
const revenueData = {
  '5D': [
    { period: 'Mon', revenue: 10.2, fill: 'var(--color-revenue)' },
    { period: 'Tue', revenue: 62.8, fill: 'var(--color-revenue)' },
    { period: 'Wed', revenue: 38.1, fill: 'var(--color-revenue)' },
    { period: 'Thu', revenue: 71.4, fill: 'var(--color-revenue)' },
    { period: 'Fri', revenue: 54.7, fill: 'var(--color-revenue)' },
    { period: 'Sat', revenue: 29.3, fill: 'var(--color-revenue)' },
    { period: 'Sun', revenue: 40.6, fill: 'var(--color-revenue)' },
  ],
  '2W': [
    { period: 'W1', revenue: 324.5, fill: 'var(--color-revenue)' },
    { period: 'W2', revenue: 398.7, fill: 'var(--color-revenue)' },
    { period: 'W3', revenue: 276.3, fill: 'var(--color-revenue)' },
    { period: 'W4', revenue: 445.9, fill: 'var(--color-revenue)' },
    { period: 'W5', revenue: 387.2, fill: 'var(--color-revenue)' },
    { period: 'W6', revenue: 512.8, fill: 'var(--color-revenue)' },
    { period: 'W7', revenue: 358.4, fill: 'var(--color-revenue)' },
    { period: 'W8', revenue: 478.6, fill: 'var(--color-revenue)' },
  ],
  '1M': [
    { period: 'W1', revenue: 324.5, fill: 'var(--color-revenue)' },
    { period: 'W2', revenue: 398.7, fill: 'var(--color-revenue)' },
    { period: 'W3', revenue: 276.3, fill: 'var(--color-revenue)' },
    { period: 'W4', revenue: 445.9, fill: 'var(--color-revenue)' },
    { period: 'W5', revenue: 387.2, fill: 'var(--color-revenue)' },
    { period: 'W6', revenue: 512.8, fill: 'var(--color-revenue)' },
    { period: 'W7', revenue: 358.4, fill: 'var(--color-revenue)' },
    { period: 'W8', revenue: 478.6, fill: 'var(--color-revenue)' },
    { period: 'W9', revenue: 423.1, fill: 'var(--color-revenue)' },
    { period: 'W10', revenue: 567.3, fill: 'var(--color-revenue)' },
    { period: 'W11', revenue: 489.7, fill: 'var(--color-revenue)' },
    { period: 'W12', revenue: 534.2, fill: 'var(--color-revenue)' },
  ],
  '6M': [
    { period: 'Jan', revenue: 1875.3, fill: 'var(--color-revenue)' },
    { period: 'Feb', revenue: 2234.7, fill: 'var(--color-revenue)' },
    { period: 'Mar', revenue: 1698.2, fill: 'var(--color-revenue)' },
    { period: 'Apr', revenue: 2567.8, fill: 'var(--color-revenue)' },
    { period: 'May', revenue: 2145.6, fill: 'var(--color-revenue)' },
    { period: 'Jun', revenue: 2789.4, fill: 'var(--color-revenue)' },
    { period: 'Jul', revenue: 2356.1, fill: 'var(--color-revenue)' },
    { period: 'Aug', revenue: 3012.5, fill: 'var(--color-revenue)' },
    { period: 'Sep', revenue: 2687.9, fill: 'var(--color-revenue)' },
    { period: 'Oct', revenue: 3234.8, fill: 'var(--color-revenue)' },
    { period: 'Nov', revenue: 2891.3, fill: 'var(--color-revenue)' },
    { period: 'Dec', revenue: 3456.7, fill: 'var(--color-revenue)' },
  ],
  '1Y': [
    { period: 'Q1 2023', revenue: 5808.2, fill: 'var(--color-revenue)' },
    { period: 'Q2 2023', revenue: 7501.8, fill: 'var(--color-revenue)' },
    { period: 'Q3 2023', revenue: 6234.7, fill: 'var(--color-revenue)' },
    { period: 'Q4 2023', revenue: 8456.3, fill: 'var(--color-revenue)' },
    { period: 'Q1 2024', revenue: 7123.9, fill: 'var(--color-revenue)' },
    { period: 'Q2 2024', revenue: 9287.5, fill: 'var(--color-revenue)' },
    { period: 'Q3 2024', revenue: 8567.1, fill: 'var(--color-revenue)' },
    { period: 'Q4 2024', revenue: 10234.6, fill: 'var(--color-revenue)' },
  ],
};

// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
  revenue: {
    label: 'Revenue',
    color: 'var(--color-violet-500)',
  },
} satisfies ChartConfig;

// Period configuration
const PERIODS = {
  '5D': { key: '5D', label: '5D' },
  '2W': { key: '2W', label: '2W' },
  '1M': { key: '1M', label: '1M' },
  '6M': { key: '6M', label: '6M' },
  '1Y': { key: '1Y', label: '1Y' },
} as const;

type PeriodKey = keyof typeof PERIODS;

export default function LineChart3() {
  const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('5D');

  // Get data for selected period
  const currentData = revenueData[selectedPeriod];

  // Calculate total revenue dynamically based on selected period
  const totalRevenue = currentData.reduce((sum, item) => sum + item.revenue, 0);

  // Format total revenue display
  const formatRevenue = (amount: number) => {
    if (amount >= 1000) {
      return `${(amount / 1000).toFixed(1)}M`;
    }
    return `${amount.toFixed(0)}k`;
  };

  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="w-full max-w-md rounded-2xl shadow-sm p-6">
        <CardHeader className="p-0 pb-6 mb-6">
          <CardTitle className="text-lg font-semibold">Revenue Performance</CardTitle>
          <CardToolbar>
            <Button variant="outline">Export</Button>
          </CardToolbar>
        </CardHeader>

        <CardContent className="p-0 space-y-6">
          {/* Nav */}
          <div className="space-y-6 mb-6">
            {/* Stats Section */}
            <div className="flex items-center gap-4">
              <div className="size-12 bg-violet-100 border border-violet-200 dark:border-violet-800 dark:bg-violet-950 rounded-full flex items-center justify-center">
                <ChartNoAxesCombined className="w-6 h-6 text-violet-600" />
              </div>
              <div>
                <div className="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1">
                  Total Revenue
                </div>
                <div className="text-2xl font-bold">${formatRevenue(totalRevenue)}</div>
              </div>
            </div>

            {/* Toggle Group */}
            <ToggleGroup
              type="single"
              value={selectedPeriod}
              variant="outline"
              onValueChange={(value) => value && setSelectedPeriod(value as PeriodKey)}
              className="w-full"
            >
              {Object.values(PERIODS).map((period) => (
                <ToggleGroupItem
                  key={period.key}
                  value={period.key}
                  variant="outline"
                  className="flex-1 data-[state=on]:bg-gray-900 data-[state=on]:text-white data-[state=on]:border-gray-900"
                >
                  {period.label}
                </ToggleGroupItem>
              ))}
            </ToggleGroup>
          </div>

          {/* Chart */}
          <div className="h-40 relative w-full overflow-hidden">
            <ChartContainer
              config={chartConfig}
              className="h-full w-full [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
            >
              <LineChart
                accessibilityLayer
                data={currentData}
                margin={{
                  top: 10,
                  left: 10,
                  right: 10,
                  bottom: 10,
                }}
              >
                <CartesianGrid
                  strokeDasharray="4 8"
                  stroke="var(--input)"
                  strokeOpacity={1}
                  horizontal={false}
                  vertical={true}
                />

                <ChartTooltip
                  cursor={{
                    stroke: chartConfig.revenue.color,
                    strokeWidth: 1,
                    strokeDasharray: '2 4',
                  }}
                  content={<ChartTooltipContent indicator="line" nameKey="revenue" hideLabel />}
                />
                <Line
                  dataKey="revenue"
                  type="natural"
                  stroke="var(--color-revenue)"
                  strokeWidth={2}
                  dot={false}
                  activeDot={{
                    r: 4,
                    fill: 'var(--color-revenue)',
                    stroke: 'var(--color-revenue)',
                    strokeWidth: 0,
                  }}
                />
              </LineChart>
            </ChartContainer>
          </div>

          {/* Footer Note */}
          <div className="flex items-center gap-2 text-xs text-muted-foreground">
            <Info className="size-3.5" />
            <span>Revenue includes subscription and one-time payments.</span>
          </div>
        </CardContent>
      </Card>
    </div>
  );
}
line-chart-4
Open in
Loading...
charts/line-charts/line-chart-4.tsx
'use client';

import React from 'react';
import { Button } from '@/registry/default/ui/button';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from '@/registry/default/ui/chart';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from '@/registry/default/ui/dropdown-menu';
import { Calendar, Download, Filter, MoreHorizontal, RefreshCw, Share2 } from 'lucide-react';
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from 'recharts';

// Social media engagement data throughout the day (percentage)
const engagementData = [
  { time: '6AM', facebook: 2, instagram: 8, linkedin: 5 },
  { time: '7AM', facebook: 5, instagram: 12, linkedin: 8 },
  { time: '8AM', facebook: 8, instagram: 18, linkedin: 15 },
  { time: '9AM', facebook: 12, instagram: 25, linkedin: 22 },
  { time: '10AM', facebook: 15, instagram: 35, linkedin: 28 },
  { time: '11AM', facebook: 18, instagram: 42, linkedin: 32 },
  { time: '12PM', facebook: 22, instagram: 38, linkedin: 35 },
  { time: '1PM', facebook: 25, instagram: 45, linkedin: 30 },
  { time: '2PM', facebook: 28, instagram: 48, linkedin: 33 },
  { time: '3PM', facebook: 30, instagram: 52, linkedin: 38 },
  { time: '4PM', facebook: 26, instagram: 46, linkedin: 35 },
  { time: '5PM', facebook: 24, instagram: 44, linkedin: 32 },
  { time: '6PM', facebook: 22, instagram: 40, linkedin: 28 },
  { time: '7PM', facebook: 20, instagram: 38, linkedin: 25 },
  { time: '8PM', facebook: 18, instagram: 35, linkedin: 22 },
];

// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
  facebook: {
    label: 'Facebook',
    color: 'var(--color-blue-600)',
  },
  instagram: {
    label: 'Instagram',
    color: 'var(--color-orange-500)',
  },
  linkedin: {
    label: 'LinkedIn',
    color: 'var(--color-slate-600)',
  },
} satisfies ChartConfig;

// Custom Tooltip
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    dataKey: string;
    value: number;
    color: string;
  }>;
  label?: string;
}

const ChartLabel = ({ label, color }: { label: string; color: string }) => {
  return (
    <div className="flex items-center gap-1.5">
      <div className="size-3.5 border-4 rounded-full bg-background" style={{ borderColor: color }}></div>
      <span className="text-muted-foreground">{label}</span>
    </div>
  );
};

const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
  if (active && payload && payload.length) {
    return (
      <div className="rounded-lg border bg-popover p-3 shadow-sm shadow-black/5 min-w-[150px]">
        <div className="text-xs font-medium text-muted-foreground tracking-wide mb-2.5">{label}</div>
        <div className="space-y-2">
          {payload.map((entry, index) => {
            const config = chartConfig[entry.dataKey as keyof typeof chartConfig];
            return (
              <div key={index} className="flex items-center gap-2 text-xs">
                <ChartLabel label={config?.label + ':'} color={entry.color} />
                <span className="font-semibold text-popover-foreground">{entry.value}%</span>
              </div>
            );
          })}
        </div>
      </div>
    );
  }
  return null;
};

// Chart Legend Component
const ChartLegend = ({ label, color }: { label: string; color: string }) => {
  return (
    <div className="flex items-center gap-2">
      <div
        className="size-3.5 border-4 rounded-full bg-background border-border"
        style={{ borderColor: `${color}` }}
      ></div>
      <span className="text-sm text-muted-foreground">{label}</span>
    </div>
  );
};

export default function LineChart4() {
  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="w-full max-w-2xl">
        <CardHeader className="border-0 pt-6 pb-4">
          <CardTitle className="text-lg font-semibold">Social Media Activity</CardTitle>
          <CardToolbar>
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <Button variant="ghost" size="sm" className="h-8 w-8 p-0">
                  <MoreHorizontal className="h-4 w-4" />
                </Button>
              </DropdownMenuTrigger>
              <DropdownMenuContent align="end">
                <DropdownMenuItem>
                  <Download className="h-4 w-4" />
                  Export Data
                </DropdownMenuItem>
                <DropdownMenuItem>
                  <Calendar className="h-4 w-4" />
                  Change Date
                </DropdownMenuItem>
                <DropdownMenuItem>
                  <Filter className="h-4 w-4" />
                  Filter Platforms
                </DropdownMenuItem>
                <DropdownMenuItem>
                  <RefreshCw className="h-4 w-4" />
                  Refresh
                </DropdownMenuItem>
                <DropdownMenuSeparator />
                <DropdownMenuItem>
                  <Share2 className="h-4 w-4" />
                  Share Report
                </DropdownMenuItem>
              </DropdownMenuContent>
            </DropdownMenu>
          </CardToolbar>
        </CardHeader>

        <CardContent className="ps-0 pe-4.5 pb-6">
          <ChartContainer
            config={chartConfig}
            className="h-[200px] w-full mb-6 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
          >
            <LineChart
              data={engagementData}
              margin={{
                top: 5,
                right: 5,
                left: 5,
                bottom: 5,
              }}
            >
              <CartesianGrid
                strokeDasharray="4 8"
                stroke="var(--input)"
                strokeOpacity={1}
                horizontal={true}
                vertical={false}
              />

              <XAxis
                dataKey="time"
                axisLine={false}
                tickLine={false}
                tick={{ fontSize: 11, fill: 'var(--text-muted-foreground)' }}
                tickMargin={10}
              />

              <YAxis
                axisLine={false}
                tickLine={false}
                tick={{ fontSize: 11, fill: 'var(--text-muted-foreground)' }}
                tickFormatter={(value) => `${value}%`}
                domain={[0, 60]}
                tickMargin={10}
              />

              <ChartTooltip content={<CustomTooltip />} cursor={{ strokeDasharray: '3 3', stroke: 'var(--input)' }} />

              {/* Facebook Line */}
              <Line dataKey="facebook" type="monotone" stroke="var(--color-facebook)" strokeWidth={2} dot={false} />

              {/* Instagram Line */}
              <Line dataKey="instagram" type="monotone" stroke="var(--color-instagram)" strokeWidth={2} dot={false} />

              {/* LinkedIn Line */}
              <Line dataKey="linkedin" type="monotone" stroke="var(--color-linkedin)" strokeWidth={2} dot={false} />
            </LineChart>
          </ChartContainer>

          {/* Legend */}
          <div className="flex items-center justify-center gap-6">
            <ChartLegend label="Facebook" color={chartConfig.facebook.color} />
            <ChartLegend label="Instagram" color={chartConfig.instagram.color} />
            <ChartLegend label="LinkedIn" color={chartConfig.linkedin.color} />
          </div>
        </CardContent>
      </Card>
    </div>
  );
}
line-chart-5
Open in
Loading...
charts/line-charts/line-chart-5.tsx
'use client';

import React, { useState } from 'react';
import { Badge } from '@/registry/default/ui/badge';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/registry/default/ui/select';
import { TrendingDown, TrendingUp } from 'lucide-react';
import { Area, CartesianGrid, ComposedChart, Line, XAxis, YAxis } from 'recharts';

// E-commerce data for different periods with balanced patterns
const salesViewsData = {
  '7d': [
    { period: 'Aug 1', sales: 850, views: 620 },
    { period: 'Aug 5', sales: 920, views: 780 },
    { period: 'Aug 7', sales: 780, views: 540 },
    { period: 'Aug 8', sales: 1240, views: 890 },
    { period: 'Aug 22', sales: 1100, views: 720 },
  ],
  '30d': [
    { period: 'Week 1', sales: 5200, views: 4800 },
    { period: 'Week 2', sales: 6400, views: 5200 },
    { period: 'Week 3', sales: 5800, views: 4600 },
    { period: 'Week 4', sales: 7200, views: 5800 },
    { period: 'Week 5', sales: 6800, views: 5400 },
    { period: 'Week 6', sales: 6200, views: 4900 },
  ],
  '90d': [
    { period: 'Jan', sales: 22000, views: 18500 },
    { period: 'Feb', sales: 24800, views: 20200 },
    { period: 'Mar', sales: 21400, views: 17800 },
    { period: 'Apr', sales: 26200, views: 21600 },
    { period: 'May', sales: 25600, views: 20800 },
    { period: 'Jun', sales: 27400, views: 22400 },
    { period: 'Jul', sales: 24800, views: 19600 },
    { period: 'Aug', sales: 23600, views: 18800 },
    { period: 'Sep', sales: 28200, views: 23200 },
  ],
  '12m': [
    { period: 'Q1 23', sales: 85000, views: 72000 },
    { period: 'Q2 23', sales: 92000, views: 78000 },
    { period: 'Q3 23', sales: 88000, views: 74000 },
    { period: 'Q4 23', sales: 98000, views: 82000 },
    { period: 'Q1 24', sales: 94000, views: 79000 },
    { period: 'Q2 24', sales: 102000, views: 86000 },
    { period: 'Q3 24', sales: 96000, views: 81000 },
    { period: 'Q4 24', sales: 108000, views: 90000 },
  ],
};

// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
  sales: {
    label: 'Sales',
    color: 'var(--color-amber-500)',
  },
  views: {
    label: 'Views',
    color: 'var(--color-purple-500)',
  },
} satisfies ChartConfig;

// Custom Tooltip
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    dataKey: string;
    value: number;
    color: string;
  }>;
  label?: string;
}

const ChartLabel = ({ label, color }: { label: string; color: string }) => {
  return (
    <div className="flex items-center gap-1.5">
      <div className="w-1 h-3 rounded-full" style={{ backgroundColor: color }}></div>
      <span className="text-muted-foreground">{label}</span>
    </div>
  );
};

const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
  if (active && payload && payload.length) {
    return (
      <div className="rounded-lg border bg-popover p-3 shadow-sm shadow-black/5 min-w-[150px]">
        <div className="text-xs font-medium text-muted-foreground tracking-wide mb-2.5">{label}</div>
        <div className="space-y-2">
          {payload.map((entry, index) => {
            const config = chartConfig[entry.dataKey as keyof typeof chartConfig];
            return (
              <div key={index} className="flex items-center gap-2 text-xs">
                <ChartLabel label={config?.label + ':'} color={entry.color} />
                <span className="font-semibold text-popover-foreground">
                  {entry.dataKey === 'sales'
                    ? `£${entry.value.toLocaleString()}`
                    : entry.value >= 0
                      ? `+${entry.value.toLocaleString()}`
                      : entry.value.toLocaleString()}
                </span>
              </div>
            );
          })}
        </div>
      </div>
    );
  }
  return null;
};

// Period configuration
const PERIODS = {
  '7d': { key: '7d', label: 'Last 7 days' },
  '30d': { key: '30d', label: 'Last 30 days' },
  '90d': { key: '90d', label: 'Last 90 days' },
  '12m': { key: '12m', label: 'Last 12 months' },
} as const;

type PeriodKey = keyof typeof PERIODS;

export default function LineChart5() {
  const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('30d');

  // Get data for selected period
  const currentData = salesViewsData[selectedPeriod];

  // Calculate totals and percentages
  const totalSales = currentData.reduce((sum, item) => sum + item.sales, 0);
  const totalViews = currentData.reduce((sum, item) => sum + item.views, 0);
  // Calculate percentage changes (simulated)
  const salesChange = selectedPeriod === '7d' ? 12 : selectedPeriod === '30d' ? 8 : selectedPeriod === '90d' ? -3 : 15;
  const viewsChange = selectedPeriod === '7d' ? -3 : selectedPeriod === '30d' ? 5 : selectedPeriod === '90d' ? -8 : 12;

  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="w-full max-w-3xl">
        <CardHeader className="border-0 min-h-auto pt-6 pb-4">
          <CardTitle className="text-lg font-semibold">E-commerce Sales</CardTitle>
          <CardToolbar>
            {/* Period Selector */}
            <Select value={selectedPeriod} onValueChange={(value) => setSelectedPeriod(value as PeriodKey)}>
              <SelectTrigger>
                <SelectValue />
              </SelectTrigger>
              <SelectContent align="end">
                {Object.values(PERIODS).map((period) => (
                  <SelectItem key={period.key} value={period.key}>
                    {period.label}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </CardToolbar>
        </CardHeader>

        <CardContent className="px-2 pb-6">
          {/* Stats Section */}
          <div className="flex items-center flex-wrap gap-3.5 md:gap-10 px-5 mb-8 text-sm">
            <div className="flex items-center gap-3.5">
              <ChartLabel label="Sales" color={chartConfig.sales.color} />
              <div className="flex items-center gap-2">
                <span className="text-2xl font-bold">£{totalSales.toLocaleString()}</span>
                <Badge variant={salesChange >= 0 ? 'success' : 'destructive'} appearance="light">
                  {salesChange >= 0 ? <TrendingUp className="size-3" /> : <TrendingDown className="size-3" />}
                  {Math.abs(salesChange)}%
                </Badge>
              </div>
            </div>
            <div className="flex items-center gap-3.5">
              <ChartLabel label="Views" color={chartConfig.views.color} />
              <div className="flex items-center gap-2">
                <span className="text-2xl font-bold">{totalViews.toLocaleString()}</span>
                <Badge variant={salesChange <= 0 ? 'success' : 'destructive'} appearance="light">
                  {viewsChange <= 0 ? <TrendingUp className="size-3" /> : <TrendingDown className="size-3" />}
                  {Math.abs(viewsChange)}%
                </Badge>
              </div>
            </div>
          </div>

          {/* Chart */}
          <ChartContainer
            config={chartConfig}
            className="h-[300px] w-full [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
          >
            <ComposedChart
              data={currentData}
              margin={{
                top: 30,
                right: 5,
                left: 5,
                bottom: 10,
              }}
            >
              {/* Background pattern for chart area only */}
              <defs>
                <linearGradient id="salesGradient" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="0%" stopColor={chartConfig.sales.color} stopOpacity={0.3} />
                  <stop offset="100%" stopColor={chartConfig.sales.color} stopOpacity={0.05} />
                </linearGradient>
                <linearGradient id="viewsGradient" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="0%" stopColor={chartConfig.views.color} stopOpacity={0.3} />
                  <stop offset="100%" stopColor={chartConfig.views.color} stopOpacity={0.05} />
                </linearGradient>
                <filter id="glow">
                  <feGaussianBlur stdDeviation="3" result="coloredBlur" />
                  <feMerge>
                    <feMergeNode in="coloredBlur" />
                    <feMergeNode in="SourceGraphic" />
                  </feMerge>
                </filter>
              </defs>

              <CartesianGrid
                strokeDasharray="4 12"
                stroke="var(--input)"
                strokeOpacity={1}
                horizontal={true}
                vertical={false}
              />

              {/* X Axis */}
              <XAxis
                dataKey="period"
                axisLine={false}
                tickLine={false}
                tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
                tickMargin={10}
              />

              {/* Left Y Axis for Sales */}
              <YAxis
                yAxisId="sales"
                orientation="left"
                axisLine={false}
                tickLine={false}
                tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
                tickFormatter={(value) => (selectedPeriod === '7d' ? `£${value}` : `£${(value / 1000).toFixed(0)}k`)}
                tickMargin={10}
              />
              {/* Right Y Axis for Views */}
              <YAxis
                yAxisId="views"
                orientation="right"
                axisLine={false}
                tickLine={false}
                tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
                tickFormatter={(value) => {
                  if (selectedPeriod === '7d') {
                    return value >= 0 ? `+${value}` : value.toString();
                  }
                  return value >= 0 ? `+${(value / 1000).toFixed(0)}k` : `${(value / 1000).toFixed(0)}k`;
                }}
                tickMargin={8}
                domain={['dataMin - 100', 'dataMax + 100']}
              />

              <ChartTooltip
                content={<CustomTooltip />}
                cursor={{ strokeDasharray: '3 3', stroke: 'var(--muted-foreground)', strokeOpacity: 0.5 }}
              />

              {/* Sales Line (Linear) */}
              <Line
                yAxisId="sales"
                type="linear"
                dataKey="sales"
                stroke={chartConfig.sales.color}
                strokeWidth={1}
                dot={false}
                activeDot={{
                  r: 5,
                  fill: chartConfig.sales.color,
                  strokeWidth: 0,
                }}
              />

              {/* Views Line (Linear Dashed) */}
              <Line
                yAxisId="views"
                type="linear"
                dataKey="views"
                stroke={chartConfig.views.color}
                strokeWidth={1}
                strokeDasharray="8 4"
                dot={false}
                activeDot={{
                  r: 5,
                  fill: chartConfig.views.color,
                  strokeWidth: 0,
                }}
              />
            </ComposedChart>
          </ChartContainer>
        </CardContent>
      </Card>
    </div>
  );
}
line-chart-6
Open in
Loading...
charts/line-charts/line-chart-6.tsx
'use client';

import React, { useState } from 'react';
import { Badge } from '@/registry/default/ui/badge';
import { Card, CardContent, CardHeader } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { ArrowDown, ArrowUp } from 'lucide-react';
import { Line, LineChart, XAxis, YAxis } from 'recharts';
import { cn } from '@/lib/utils';

// E-commerce platform metrics data (adapted from interactive chart example)
const platformData = [
  { date: '2024-04-01', orders: 222, response: 150, revenue: 8.2, customers: 420 },
  { date: '2024-04-02', orders: 97, response: 180, revenue: 4.5, customers: 290 },
  { date: '2024-04-03', orders: 167, response: 120, revenue: 6.8, customers: 380 },
  { date: '2024-04-04', orders: 242, response: 260, revenue: 9.1, customers: 520 },
  { date: '2024-04-05', orders: 301, response: 340, revenue: 11.2, customers: 620 },
  { date: '2024-04-06', orders: 59, response: 110, revenue: 2.8, customers: 180 },
  { date: '2024-04-07', orders: 261, response: 190, revenue: 9.8, customers: 510 },
  { date: '2024-04-08', orders: 327, response: 350, revenue: 12.1, customers: 650 },
  { date: '2024-04-09', orders: 89, response: 150, revenue: 3.8, customers: 220 },
  { date: '2024-04-10', orders: 195, response: 165, revenue: 7.2, customers: 390 },
  { date: '2024-04-11', orders: 224, response: 170, revenue: 8.5, customers: 450 },
  { date: '2024-04-12', orders: 387, response: 290, revenue: 13.8, customers: 710 },
  { date: '2024-04-13', orders: 215, response: 250, revenue: 8.2, customers: 430 },
  { date: '2024-04-14', orders: 75, response: 130, revenue: 3.1, customers: 190 },
  { date: '2024-04-15', orders: 122, response: 180, revenue: 5.1, customers: 300 },
  { date: '2024-04-16', orders: 197, response: 160, revenue: 7.5, customers: 390 },
  { date: '2024-04-17', orders: 473, response: 380, revenue: 17.2, customers: 890 },
  { date: '2024-04-18', orders: 338, response: 400, revenue: 12.9, customers: 670 },
];

// Metric configurations
const metrics = [
  {
    key: 'orders',
    label: 'Orders',
    value: 2865,
    previousValue: 2420,
    format: (val: number) => val.toLocaleString(),
  },
  {
    key: 'response',
    label: 'Response Time',
    value: 135,
    previousValue: 118,
    format: (val: number) => `${val}ms`,
    isNegative: true, // Lower response time is better
  },
  {
    key: 'revenue',
    label: 'Revenue',
    value: 8.67,
    previousValue: 7.54,
    format: (val: number) => `$${val.toFixed(2)}k`,
  },
  {
    key: 'customers',
    label: 'Active Users',
    value: 1425,
    previousValue: 1240,
    format: (val: number) => val.toLocaleString(),
  },
];

// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const chartConfig = {
  orders: {
    label: 'Orders',
    color: 'var(--color-teal-500)',
  },
  response: {
    label: 'Response Time',
    color: 'var(--color-violet-500)',
  },
  revenue: {
    label: 'Revenue',
    color: 'var(--color-lime-500)',
  },
  customers: {
    label: 'Active Users',
    color: 'var(--color-sky-500)',
  },
} satisfies ChartConfig;

// Custom Tooltip
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    dataKey: string;
    value: number;
    color: string;
  }>;
  label?: string;
}

const CustomTooltip = ({ active, payload }: TooltipProps) => {
  if (active && payload && payload.length) {
    const entry = payload[0];
    const metric = metrics.find((m) => m.key === entry.dataKey);

    if (metric) {
      return (
        <div className="rounded-lg border bg-popover p-3 shadow-sm shadow-black/5 min-w-[120px]">
          <div className="flex items-center gap-2 text-sm">
            <div className="size-1.5 rounded-full" style={{ backgroundColor: entry.color }}></div>
            <span className="text-muted-foreground">{metric.label}:</span>
            <span className="font-semibold text-popover-foreground">{metric.format(entry.value)}</span>
          </div>
        </div>
      );
    }
  }
  return null;
};

export default function LineChart6() {
  const [selectedMetric, setSelectedMetric] = useState<string>('response');

  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="@container w-full max-w-4xl">
        <CardHeader className="p-0 mb-5">
          {/* Metrics Grid */}
          <div className="grid @2xl:grid-cols-2 @3xl:grid-cols-4 grow">
            {metrics.map((metric) => {
              const change = ((metric.value - metric.previousValue) / metric.previousValue) * 100;
              const isPositive = metric.isNegative ? change < 0 : change > 0;

              return (
                <button
                  key={metric.key}
                  onClick={() => setSelectedMetric(metric.key)}
                  className={cn(
                    'cursor-pointer flex-1 text-start p-4 last:border-b-0 border-b @2xl:border-b @2xl:even:border-e @3xl:border-b-0 @3xl:border-e @3xl:last:border-e-0 transition-all',
                    selectedMetric === metric.key && 'bg-muted/50',
                  )}
                >
                  <div className="flex items-center justify-between mb-2">
                    <span className="text-sm text-muted-foreground">{metric.label}</span>
                    <Badge variant={isPositive ? 'success' : 'destructive'} appearance="outline">
                      {isPositive ? <ArrowUp className="size-3" /> : <ArrowDown className="size-3" />}
                      {Math.abs(change).toFixed(1)}%
                    </Badge>
                  </div>
                  <div className="text-2xl font-bold">{metric.format(metric.value)}</div>
                  <div className="text-xs text-muted-foreground mt-1">from {metric.format(metric.previousValue)}</div>
                </button>
              );
            })}
          </div>
        </CardHeader>

        <CardContent className="px-2.5 py-6">
          <ChartContainer
            config={chartConfig}
            className="h-96 w-full overflow-visible [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
          >
            <LineChart
              data={platformData}
              margin={{
                top: 20,
                right: 20,
                left: 5,
                bottom: 20,
              }}
              style={{ overflow: 'visible' }}
            >
              {/* Background pattern for chart area only */}
              <defs>
                <pattern id="dotGrid" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
                  <circle cx="10" cy="10" r="1" fill="var(--input)" fillOpacity="1" />
                </pattern>
                <filter id="lineShadow" x="-100%" y="-100%" width="300%" height="300%">
                  <feDropShadow
                    dx="4"
                    dy="6"
                    stdDeviation="25"
                    floodColor={`${chartConfig[selectedMetric as keyof typeof chartConfig]?.color}60`}
                  />
                </filter>
                <filter id="dotShadow" x="-50%" y="-50%" width="200%" height="200%">
                  <feDropShadow dx="2" dy="2" stdDeviation="3" floodColor="rgba(0,0,0,0.5)" />
                </filter>
              </defs>

              <XAxis
                dataKey="date"
                axisLine={false}
                tickLine={false}
                tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
                tickMargin={10}
                tickFormatter={(value) => {
                  const date = new Date(value);
                  return date.toLocaleDateString('en-US', {
                    month: 'short',
                    day: 'numeric',
                  });
                }}
              />

              <YAxis
                axisLine={false}
                tickLine={false}
                tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
                tickMargin={10}
                tickCount={6}
                tickFormatter={(value) => {
                  const metric = metrics.find((m) => m.key === selectedMetric);
                  return metric ? metric.format(value) : value.toString();
                }}
              />

              <ChartTooltip content={<CustomTooltip />} cursor={{ strokeDasharray: '3 3', stroke: '#9ca3af' }} />

              {/* Background pattern for chart area only */}
              <rect
                x="60px"
                y="-20px"
                width="calc(100% - 75px)"
                height="calc(100% - 10px)"
                fill="url(#dotGrid)"
                style={{ pointerEvents: 'none' }}
              />

              <Line
                type="monotone"
                dataKey={selectedMetric}
                stroke={chartConfig[selectedMetric as keyof typeof chartConfig]?.color}
                strokeWidth={2}
                filter="url(#lineShadow)"
                dot={false}
                activeDot={{
                  r: 6,
                  fill: chartConfig[selectedMetric as keyof typeof chartConfig]?.color,
                  stroke: 'white',
                  strokeWidth: 2,
                  filter: 'url(#dotShadow)',
                }}
              />
            </LineChart>
          </ChartContainer>
        </CardContent>
      </Card>
    </div>
  );
}
line-chart-7
Open in
Loading...
charts/line-charts/line-chart-7.tsx
'use client';

import React, { useState } from 'react';
import { Button } from '@/registry/default/ui/button';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { ToggleGroup, ToggleGroupItem } from '@/registry/default/ui/toggle-group';
import { ArrowDownRight, ArrowUpRight, Settings } from 'lucide-react';
import { Line, LineChart, XAxis, YAxis } from 'recharts';
import { cn } from '@/lib/utils';

// NFT Collection analytics data for different time periods
const nftData = {
  week: [
    { period: 'Mon', value: 2.1 },
    { period: 'Tue', value: 4.2 },
    { period: 'Wed', value: 2.8 },
    { period: 'Thu', value: 5.1 },
    { period: 'Fri', value: 3.3 },
    { period: 'Sat', value: 6.2 },
    { period: 'Sun', value: 4.9 },
  ],
  month: [
    { period: 'Jan', value: 1.2 },
    { period: 'Feb', value: 2.8 },
    { period: 'Mar', value: 1.9 },
    { period: 'Apr', value: 3.4 },
    { period: 'May', value: 2.7 },
    { period: 'Jun', value: 4.1 },
    { period: 'Jul', value: 3.2 },
    { period: 'Aug', value: 5.4 },
    { period: 'Sep', value: 4.8 },
    { period: 'Oct', value: 6.1 },
    { period: 'Nov', value: 5.2 },
    { period: 'Dec', value: 6.8 },
  ],
  max: [
    { period: '2018', value: 0.1 },
    { period: '2019', value: 0.4 },
    { period: '2020', value: 0.7 },
    { period: '2021', value: 2.1 },
    { period: '2022', value: 3.2 },
    { period: '2023', value: 4.2 },
    { period: '2024', value: 6.4 },
  ],
};

type PeriodKey = keyof typeof nftData;

const PERIODS = {
  week: { key: 'week', label: 'Week' },
  month: { key: 'month', label: 'Month' },
  max: { key: 'max', label: 'Max' },
} as const;

// Chart configuration with emerald theme
const chartConfig = {
  value: {
    label: 'NFT Floor Price',
    color: 'var(--color-emerald-500)',
  },
} satisfies ChartConfig;

// Custom Tooltip
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    dataKey: string;
    value: number;
    color: string;
  }>;
  label?: string;
}

const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
  if (active && payload && payload.length) {
    return (
      <div className="rounded-lg border border-slate-700 bg-slate-800 p-3 shadow-md shadow-slate-100/5 min-w-[120px]">
        <div className="text-xs font-medium text-white/60 tracking-wide mb-2">{label}</div>
        <div className="text-sm font-semibold text-white">{payload[0].value} USD</div>
      </div>
    );
  }
  return null;
};

export default function LineChart7() {
  const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('max');

  const currentData = nftData[selectedPeriod];
  const currentValue = currentData[currentData.length - 1]?.value || 0;
  const previousValue = currentData[currentData.length - 2]?.value || 0;
  const growth = previousValue > 0 ? ((currentValue - previousValue) / previousValue) * 100 : 0;

  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="w-full rounded-3xl lg:max-w-lg bg-gradient-to-br from-slate-900 via-slate-900 to-emerald-950/90 border-slate-700 text-white shadow-2xl shadow-emerald-500/10">
        <CardHeader className="border-0 min-h-auto pt-6 pb-6">
          <CardTitle className="text-lg font-semibold">Sales Analytics</CardTitle>
          <CardToolbar>
            <Button variant="outline" size="icon-sm">
              <Settings className="size-4 text-slate-200" />
            </Button>
          </CardToolbar>
        </CardHeader>

        <CardContent className="px-6 pb-6 space-y-6">
          {/* Toggle Period Selector */}
          <ToggleGroup
            type="single"
            value={selectedPeriod}
            onValueChange={(value) => value && setSelectedPeriod(value as PeriodKey)}
            className="w-full rounded-2xl border border-slate-700/80 p-1.5"
          >
            {Object.values(PERIODS).map((period) => (
              <ToggleGroupItem
                key={period.key}
                value={period.key}
                className="h-10 shadow-none! rounded-xl flex-1 border-0 data-[state=on]:bg-slate-800 data-[state=on]:text-emerald-400 text-zinc-400 hover:bg-transparent hover:text-emerald-300"
              >
                {period.label}
              </ToggleGroupItem>
            ))}
          </ToggleGroup>

          {/* Chart Container */}
          <div className="relative">
            <ChartContainer
              config={chartConfig}
              className="h-[300px] w-full [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
            >
              <LineChart
                data={currentData}
                margin={{
                  top: 10,
                  right: 10,
                  left: 10,
                  bottom: 10,
                }}
              >
                <XAxis
                  dataKey="period"
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 12, strokeWidth: 0.5, stroke: 'rgb(255, 255, 255)', opacity: 0.4 }}
                  tickMargin={20}
                  interval="preserveStartEnd"
                />

                <YAxis hide domain={['dataMin - 0.5', 'dataMax + 0.5']} />

                <ChartTooltip
                  content={<CustomTooltip />}
                  cursor={{ strokeDasharray: '2 2', stroke: 'rgb(16 185 129)', strokeOpacity: 0.6 }}
                />

                {/* Background pattern for chart area only */}
                <defs>
                  <pattern id="dotGridDark" x="0" y="0" width="36" height="36" patternUnits="userSpaceOnUse">
                    <circle cx="15" cy="15" r="1.5" fill="#ffffff" fillOpacity="0.1" />
                  </pattern>
                </defs>

                <rect
                  x="10px"
                  y="-30px"
                  width="100%"
                  height="100%"
                  fill="url(#dotGridDark)"
                  style={{ pointerEvents: 'none' }}
                />

                {/* Main line with sharp angles */}
                <Line
                  type="linear"
                  dataKey="value"
                  stroke="rgb(16 185 129)"
                  strokeWidth={3}
                  dot={{
                    r: 4,
                    fill: 'rgb(16 185 129)',
                    stroke: 'rgb(16 185 129)',
                    strokeWidth: 2,
                    filter: 'drop-shadow(0 0 6px rgb(16 185 129))',
                  }}
                  activeDot={{
                    r: 6,
                    stroke: 'rgb(16 185 129)',
                    strokeWidth: 3,
                    fill: 'rgb(16 185 129)',
                    filter: 'drop-shadow(0 0 8px rgb(16 185 129))',
                  }}
                />

                {/* Endpoint dot */}
                <Line
                  type="linear"
                  dataKey="value"
                  stroke="transparent"
                  strokeWidth={0}
                  dot={false}
                  activeDot={{
                    r: 7,
                    stroke: 'rgb(16 185 129)',
                    strokeWidth: 4,
                    fill: 'rgb(16 185 129)',
                    filter: 'drop-shadow(0 0 10px rgb(16 185 129))',
                  }}
                />
              </LineChart>
            </ChartContainer>
          </div>

          {/* Statistics */}
          <div className="flex items-center gap-2.5 justify-between">
            {/* Floor Price */}
            <div className="flex flex-col gap-0.5">
              <div className="text-sm text-slate-300">Floor Price (USD)</div>
              <div className="flex items-start gap-2">
                <span className="text-4xl font-bold text-white">{currentValue.toFixed(1)}</span>
                <span className="text-sm text-emerald-400 font-medium pt-0.75">+{(currentValue - 2.5).toFixed(1)}</span>
              </div>
            </div>

            {/* Growth */}
            <div className={cn('flex items-center gap-1.5', growth > 0 ? 'text-emerald-400' : 'text-destructive')}>
              {growth > 0 ? <ArrowUpRight className="size-6" /> : <ArrowDownRight className="size-6" />}
              <span className="text-2xl font-semibold">{(growth * 2.1).toFixed(1)}%</span>
            </div>
          </div>
        </CardContent>
      </Card>
    </div>
  );
}
line-chart-8
Open in
Loading...
charts/line-charts/line-chart-8.tsx
'use client';

import React from 'react';
import { Card, CardContent } from '@/registry/default/ui/card';
import { Line, LineChart, ReferenceLine, ResponsiveContainer, Tooltip, YAxis } from 'recharts';

// Business Case 1: SaaS Revenue Tracking (Detailed wavy pattern with micro-fluctuations)
const revenueData = [
  { value: 15000 },
  { value: 18000 },
  { value: 25000 },
  { value: 32000 },
  { value: 35000 },
  { value: 28000 },
  { value: 20000 },
  { value: 12000 },
  { value: 5000 },
  { value: -2000 },
  { value: -10000 },
  { value: -18000 },
  { value: -25000 },
  { value: -22000 },
  { value: -15000 },
  { value: -8000 },
  { value: 0 },
  { value: 8000 },
  { value: 20000 },
  { value: 28000 },
  { value: 40000 },
  { value: 48000 },
  { value: 50000 },
  { value: 45000 },
  { value: 35000 },
  { value: 25000 },
  { value: 15000 },
  { value: 2000 },
  { value: -5000 },
  { value: -12000 },
  { value: -20000 },
  { value: -28000 },
  { value: -30000 },
  { value: -25000 },
  { value: -15000 },
  { value: -5000 },
  { value: 10000 },
  { value: 22000 },
  { value: 35000 },
  { value: 45000 },
  { value: 55000 },
  { value: 52000 },
  { value: 45000 },
  { value: 35000 },
  { value: 25000 },
  { value: 12000 },
  { value: 5000 },
  { value: -8000 },
  { value: -15000 },
  { value: -12000 },
  { value: -5000 },
  { value: 3000 },
  { value: 15000 },
  { value: 25000 },
  { value: 20000 },
  { value: 10000 },
  { value: -2000 },
  { value: -15000 },
  { value: -20000 },
  { value: -15000 },
];

// Business Case 2: E-commerce Conversion Rate (Detailed sine wave with micro-variations)
const conversionData = [
  { value: 0 },
  { value: 0.8 },
  { value: 1.5 },
  { value: 2.2 },
  { value: 2.8 },
  { value: 3.2 },
  { value: 3.5 },
  { value: 3.4 },
  { value: 3.2 },
  { value: 2.6 },
  { value: 2.0 },
  { value: 1.2 },
  { value: 0.5 },
  { value: -0.2 },
  { value: -1.2 },
  { value: -1.8 },
  { value: -2.5 },
  { value: -2.8 },
  { value: -3.0 },
  { value: -2.9 },
  { value: -2.8 },
  { value: -2.2 },
  { value: -1.5 },
  { value: -0.8 },
  { value: 0.2 },
  { value: 1.0 },
  { value: 2.0 },
  { value: 2.8 },
  { value: 3.5 },
  { value: 3.9 },
  { value: 4.2 },
  { value: 4.1 },
  { value: 3.8 },
  { value: 3.2 },
  { value: 2.5 },
  { value: 1.5 },
  { value: 0.8 },
  { value: 0.2 },
  { value: -1.0 },
  { value: -1.6 },
  { value: -2.5 },
  { value: -2.9 },
  { value: -3.2 },
  { value: -3.0 },
  { value: -2.0 },
  { value: -1.2 },
  { value: 0 },
  { value: 1.2 },
  { value: 2.5 },
  { value: 3.5 },
  { value: 4.0 },
  { value: 3.8 },
  { value: 2.8 },
  { value: 1.5 },
  { value: 0.5 },
  { value: -0.8 },
  { value: -2.0 },
  { value: -2.8 },
  { value: -2.5 },
  { value: -1.0 },
];

// Business Case 3: Server Performance Monitoring (Detailed oscillating decline with volatility)
const performanceData = [
  { value: 5 },
  { value: 8 },
  { value: 10 },
  { value: 12 },
  { value: 8 },
  { value: 5 },
  { value: 3 },
  { value: 0 },
  { value: -2 },
  { value: -5 },
  { value: -8 },
  { value: -10 },
  { value: -12 },
  { value: -10 },
  { value: -8 },
  { value: -5 },
  { value: -3 },
  { value: 0 },
  { value: 2 },
  { value: 4 },
  { value: 6 },
  { value: 7 },
  { value: 4 },
  { value: 1 },
  { value: -1 },
  { value: -4 },
  { value: -6 },
  { value: -8 },
  { value: -10 },
  { value: -11 },
  { value: -12 },
  { value: -10 },
  { value: -8 },
  { value: -6 },
  { value: -4 },
  { value: -2 },
  { value: 1 },
  { value: 3 },
  { value: 5 },
  { value: 6 },
  { value: 3 },
  { value: 0 },
  { value: -2 },
  { value: -5 },
  { value: -7 },
  { value: -9 },
  { value: -11 },
  { value: -13 },
  { value: -15 },
  { value: -13 },
  { value: -11 },
  { value: -8 },
  { value: -5 },
  { value: -2 },
  { value: 0 },
  { value: -3 },
  { value: -6 },
  { value: -9 },
  { value: -12 },
  { value: -15 },
];

// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const businessCards = [
  {
    title: 'Revenue Variance',
    metric: 'Monthly change from baseline',
    baseValue: '+15K',
    baseCurrency: 'Start',
    targetValue: '-15K',
    targetCurrency: 'End',
    data: revenueData,
    change: 'Volatile',
    isPositive: false,
    color: 'var(--color-blue-500)',
  },
  {
    title: 'Conversion Change',
    metric: 'Rate variance from zero',
    baseValue: '50%',
    baseCurrency: 'Baseline',
    targetValue: '+60.5%',
    targetCurrency: 'Current',
    data: conversionData,
    change: 'Cyclical',
    isPositive: true,
    color: 'var(--color-emerald-500)',
  },
  {
    title: 'Performance Drift',
    metric: 'System variance tracking',
    baseValue: '+5%',
    baseCurrency: 'Peak',
    targetValue: '-15%',
    targetCurrency: 'Low',
    data: performanceData,
    change: 'Declining',
    isPositive: false,
    color: 'var(--color-amber-500)',
  },
];

export default function LineChart8() {
  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-12">
      {/* Container */}
      <div className="@container grow w-full max-w-7xl">
        {/* Grid */}
        <div className="grid grid-cols-1 @3xl:grid-cols-3 gap-6">
          {/* Business Cards */}
          {businessCards.map((card, i) => (
            <Card key={i}>
              <CardContent className="flex flex-col gap-6">
                {/* Header */}
                <div className="flex flex-col">
                  <h3 className="text-base font-semibold text-foreground m-0">{card.title}</h3>
                  <p className="text-sm text-muted-foreground m-0">{card.metric}</p>
                </div>

                {/* Chart Section */}
                <div className="flex items-center justify-between">
                  {/* Left side - Base Currency */}
                  <div className="text-center">
                    <div className="text-lg font-semibold text-foreground">{card.baseValue}</div>
                    <div className="text-xs text-muted-foreground font-medium">{card.baseCurrency}</div>
                  </div>

                  {/* Center - Mini Chart */}
                  <div className="flex-1 h-14 mx-6 relative">
                    <ResponsiveContainer width="100%" height="100%">
                      <LineChart
                        data={card.data}
                        margin={{
                          top: 10,
                          right: 10,
                          left: 10,
                          bottom: 10,
                        }}
                      >
                        <YAxis domain={['dataMin', 'dataMax']} hide={true} />
                        <ReferenceLine y={0} stroke="var(--input)" strokeWidth={1} strokeDasharray="3 3" />
                        <Tooltip
                          cursor={{ stroke: card.color, strokeWidth: 1, strokeDasharray: '2 2' }}
                          position={{ x: undefined, y: undefined }}
                          offset={10}
                          allowEscapeViewBox={{ x: true, y: true }}
                          content={({ active, payload, coordinate }) => {
                            if (active && payload && payload.length && coordinate) {
                              const value = payload[0].value;
                              const formatValue = (val: number) => {
                                if (card.title === 'Revenue Variance') {
                                  return val >= 0 ? `+$${val.toLocaleString()}` : `-$${Math.abs(val).toLocaleString()}`;
                                } else if (card.title === 'Conversion Change') {
                                  return val >= 0 ? `+${val.toFixed(1)}%` : `${val.toFixed(1)}%`;
                                } else {
                                  return val >= 0 ? `+${val}%` : `${val}%`;
                                }
                              };

                              // Smart positioning logic
                              const tooltipStyle: React.CSSProperties = {
                                transform:
                                  coordinate.x && coordinate.x > 120 ? 'translateX(-100%)' : 'translateX(10px)',
                                marginTop: coordinate.y && coordinate.y > 30 ? '-40px' : '10px',
                              };

                              return (
                                <div
                                  className="bg-background/95 backdrop-blur-sm border border-border shadow-xl rounded-lg p-2.5 pointer-events-none z-50"
                                  style={tooltipStyle}
                                >
                                  <p className="text-sm font-semibold text-foreground leading-tight mb-1.5">
                                    {formatValue(value as number)}
                                  </p>
                                  <p className="text-xs text-muted-foreground leading-tight">{card.title}</p>
                                </div>
                              );
                            }
                            return null;
                          }}
                        />
                        <Line
                          type="monotone"
                          dataKey="value"
                          stroke={card.color}
                          strokeWidth={2}
                          dot={{
                            r: 0,
                            strokeWidth: 0,
                          }}
                          activeDot={{
                            r: 5,
                            fill: card.color,
                            stroke: 'white',
                            strokeWidth: 2,
                            filter: `drop-shadow(0 0 6px ${card.color})`,
                          }}
                        />
                      </LineChart>
                    </ResponsiveContainer>
                  </div>

                  {/* Right side - Target Currency */}
                  <div className="text-center">
                    <div className="text-lg font-semibold text-foreground">{card.targetValue}</div>
                    <div className="text-xs text-muted-foreground font-medium">{card.targetCurrency}</div>
                  </div>
                </div>
              </CardContent>
            </Card>
          ))}
        </div>
      </div>
    </div>
  );
}
line-chart-9
Open in
Loading...
charts/line-charts/line-chart-9.tsx
'use client';

import React from 'react';
import { Card, CardContent } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { TrendingUp } from 'lucide-react';
import { CartesianGrid, ComposedChart, Line, ReferenceLine, XAxis, YAxis } from 'recharts';

// E-commerce revenue tracking with seasonal fluctuations
const portfolioData = [
  { date: 'Jan 1', value: 850, time: '20:00' },
  { date: 'Jan 2', value: 1100, time: '00:00' },
  { date: 'Jan 3', value: 1680, time: '04:00' },
  { date: 'Jan 4', value: 1490, time: '08:00' },
  { date: 'Jan 5', value: 2020, time: '12:00' },
  { date: 'Jan 6', value: 2080, time: '16:00' },
  { date: 'Jan 7', value: 2180, time: '20:00' },
  { date: 'Jan 8', value: 2250, time: '00:00' },
  { date: 'Jan 9', value: 2480, time: '04:00' },
  { date: 'Jan 10', value: 2290, time: '08:00' },
  { date: 'Jan 11', value: 2450, time: '12:00' },
  { date: 'Jan 12', value: 2380, time: '16:00' },
  { date: 'Jan 13', value: 2220, time: '20:00' },
  { date: 'Jan 14', value: 1980, time: '00:00' },
  { date: 'Jan 15', value: 1750, time: '04:00' },
  { date: 'Jan 16', value: 1620, time: '08:00' },
  { date: 'Jan 17', value: 1480, time: '12:00' },
  { date: 'Jan 18', value: 1580, time: '16:00' },
  { date: 'Jan 19', value: 1820, time: '20:00' },
  { date: 'Jan 20', value: 1950, time: '00:00' },
  { date: 'Jan 21', value: 2080, time: '04:00' },
  { date: 'Jan 22', value: 2220, time: '08:00' },
  { date: 'Jan 23', value: 2380, time: '12:00' },
  { date: 'Jan 24', value: 2550, time: '16:00' },
  { date: 'Jan 25', value: 2480, time: '20:00' },
  { date: 'Jan 26', value: 2720, time: '00:00' },
  { date: 'Jan 27', value: 2900, time: '04:00' },
  { date: 'Jan 28', value: 2550, time: '08:00' },
  { date: 'Jan 29', value: 2320, time: '12:00' },
  { date: 'Feb 15', value: 2250, time: '14:00' },
  { date: 'Mar 24', value: 1900, time: '16:00' },
];

// Chart configuration
const chartConfig = {
  value: {
    label: 'Balance',
    color: 'var(--color-purple-500)',
  },
} satisfies ChartConfig;

// Calculate portfolio metrics
const currentBalance = 24847.83;
const todaysPnL = 1249.0;
const pnlPercentage = 8;
const highValue = Math.max(...portfolioData.map((d) => d.value));
const lowValue = Math.min(...portfolioData.map((d) => d.value));
const change = -0.082;

// Custom Tooltip
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    payload: {
      date: string;
      value: number;
    };
  }>;
  label?: string;
}

const CustomTooltip = ({ active, payload }: TooltipProps) => {
  if (active && payload && payload.length) {
    const data = payload[0].payload;
    return (
      <div className="bg-popover border border-border rounded-lg p-3 shadow-lg">
        <div className="text-sm text-muted-foreground mb-1">{data.date}</div>
        <div className="flex items-center gap-2">
          <div className="text-base font-bold">${(data.value * 10).toLocaleString()}.00</div>
          <div className="text-[11px] text-emerald-600">+12.7%</div>
        </div>
      </div>
    );
  }
  return null;
};

export default function LineChart9() {
  return (
    <div className="min-h-screen flex items-center justify-center p-6">
      <Card className="w-full max-w-5xl">
        <CardContent className="flex flex-col items-stretch gap-5">
          {/* Header */}
          <div className="mb-5">
            <h1 className="text-base text-muted-foreground font-medium mb-1">Current Balance</h1>
            <div className="flex flex-wrap items-baseline gap-1.5 sm:gap-3.5">
              <span className="text-4xl font-bold">${currentBalance.toLocaleString()}</span>
              <div className="flex items-center gap-1 text-emerald-600">
                <TrendingUp className="w-4 h-4" />
                <span className="font-medium">+12.7%</span>
                <span className="text-muted-foreground font-normal">Last 24 hours</span>
              </div>
            </div>
          </div>

          <div className="grow">
            {/* Stats Row */}
            <div className="flex items-center justify-between flex-wrap gap-2.5 text-sm mb-2.5">
              {/* Today's Sales */}
              <div className="flex items-center gap-6">
                <div className="flex items-center gap-2">
                  <span className="text-muted-foreground">Today&apos;s Sales:</span>
                  <span className="font-semibold">${todaysPnL.toLocaleString()}</span>
                  <div className="flex items-center gap-1 text-emerald-600">
                    <TrendingUp className="w-3 h-3" />
                    <span>(+{pnlPercentage}%)</span>
                  </div>
                </div>
              </div>

              {/* Stats Row */}
              <div className="flex items-center gap-6 text-muted-foreground">
                <span>
                  High: <span className="text-sky-600 font-medium">{highValue.toLocaleString()}.08</span>
                </span>
                <span>
                  Low: <span className="text-yellow-600 font-medium">{lowValue.toLocaleString()}.42</span>
                </span>
                <span>
                  Change: <span className="text-red-600 font-medium">{change}%</span>
                </span>
              </div>
            </div>

            {/* Chart */}
            <ChartContainer
              config={chartConfig}
              className="h-96 w-full [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
            >
              <ComposedChart
                data={portfolioData}
                margin={{
                  top: 20,
                  right: 10,
                  left: 5,
                  bottom: 20,
                }}
              >
                <defs>
                  <linearGradient id="areaGradient" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stopColor={chartConfig.value.color} stopOpacity={0.1} />
                    <stop offset="100%" stopColor={chartConfig.value.color} stopOpacity={0} />
                  </linearGradient>
                  <pattern id="dotGrid" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
                    <circle cx="10" cy="10" r="1" fill="var(--input)" fillOpacity="0.3" />
                  </pattern>
                  <filter id="dotShadow" x="-50%" y="-50%" width="200%" height="200%">
                    <feDropShadow dx="2" dy="3" stdDeviation="3" floodColor="rgba(0,0,0,0.8)" />
                  </filter>
                  <filter id="lineShadow" x="-100%" y="-100%" width="300%" height="300%">
                    <feDropShadow dx="4" dy="6" stdDeviation="25" floodColor="rgba(59, 130, 246, 0.9)" />
                  </filter>
                </defs>

                <rect x="0" y="0" width="100%" height="100%" fill="url(#dotGrid)" style={{ pointerEvents: 'none' }} />

                <CartesianGrid
                  strokeDasharray="4 8"
                  stroke="var(--input)"
                  strokeOpacity={1}
                  horizontal={true}
                  vertical={false}
                />

                {/* Active tick reference line */}
                <ReferenceLine x="Jan 17" stroke={chartConfig.value.color} strokeDasharray="4 4" strokeWidth={1} />

                <XAxis
                  dataKey="date"
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 12, fill: chartConfig.value.color }}
                  tickMargin={15}
                  interval="preserveStartEnd"
                  tickCount={5}
                />

                <YAxis
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 12, fill: chartConfig.value.color }}
                  tickFormatter={(value) => `$${(value * 2).toLocaleString()}`}
                  tickMargin={15}
                />

                <ChartTooltip
                  content={<CustomTooltip />}
                  cursor={{ strokeDasharray: '3 3', stroke: 'var(--muted-foreground)', strokeOpacity: 0.5 }}
                />

                <Line
                  type="monotone"
                  dataKey="value"
                  stroke={chartConfig.value.color}
                  strokeWidth={2}
                  filter="url(#lineShadow)"
                  dot={(props) => {
                    const { cx, cy, payload } = props;
                    if (payload.date === 'Jan 17' || payload.value > 2800 || payload.value < 1000) {
                      return (
                        <circle
                          key={`dot-${payload.date}`}
                          cx={cx}
                          cy={cy}
                          r={6}
                          fill={chartConfig.value.color}
                          stroke="white"
                          strokeWidth={2}
                          filter="url(#dotShadow)"
                        />
                      );
                    }

                    return <g key={`dot-${payload.date}`} />; // Return empty group for other points
                  }}
                  activeDot={{
                    r: 6,
                    fill: chartConfig.value.color,
                    stroke: 'white',
                    strokeWidth: 2,
                    filter: 'url(#dotShadow)',
                  }}
                />
              </ComposedChart>
            </ChartContainer>
          </div>
        </CardContent>
      </Card>
    </div>
  );
}

Area Charts

5 free modern area charts designed to present key metrics and insights. Each chart features unique layouts, data visualizations, and styling options. Perfect for dashboards, admin panels, and analytics pages.

View all →
area-chart-1
Open in
Loading...
charts/area-charts/area-chart-1.tsx
'use client';

import React from 'react';
import { Card, CardContent } from '@/registry/default/ui/card';
import { CircleDollarSign, TrendingUp, UserPlus } from 'lucide-react';
import { Area, AreaChart, ResponsiveContainer, Tooltip } from 'recharts';

// Business Case 1: SaaS Revenue Tracking
const revenueData = [
  { value: 1000 },
  { value: 4500 },
  { value: 2000 },
  { value: 5200 },
  { value: 1500 },
  { value: 6100 },
  { value: 3000 },
  { value: 6800 },
  { value: 2000 },
  { value: 1000 },
  { value: 4000 },
  { value: 2000 },
  { value: 3000 },
  { value: 2000 },
  { value: 6238 },
];

// Business Case 2: New Customer Acquisition
const customersData = [
  { value: 2000 },
  { value: 4500 },
  { value: 2000 },
  { value: 5200 },
  { value: 1500 },
  { value: 5100 },
  { value: 2500 },
  { value: 6800 },
  { value: 1800 },
  { value: 1000 },
  { value: 3000 },
  { value: 2000 },
  { value: 2700 },
  { value: 2000 },
  { value: 4238 },
];

// Business Case 3: Monthly Active Users
const activeUsersData = [
  { value: 2000 },
  { value: 3500 },
  { value: 2000 },
  { value: 5200 },
  { value: 1200 },
  { value: 4100 },
  { value: 3500 },
  { value: 5800 },
  { value: 2000 },
  { value: 800 },
  { value: 3000 },
  { value: 1000 },
  { value: 4000 },
  { value: 2000 },
  { value: 4238 },
];

// Business cards configuration
// Use custom or Tailwind standard colors: https://tailwindcss.com/docs/colors
const businessCards = [
  {
    title: 'Revenue',
    period: 'Last 28 days',
    value: '6.238$',
    timestamp: '',
    data: revenueData,
    color: 'var(--color-emerald-500)',
    icon: CircleDollarSign,
    gradientId: 'revenueGradient',
  },
  {
    title: 'New Customers',
    period: 'Last 28 days',
    value: '6.202',
    timestamp: '3h ago',
    data: customersData,
    color: 'var(--color-blue-500)',
    icon: UserPlus,
    gradientId: 'customersGradient',
  },
  {
    title: 'Active Users',
    period: 'Last 28 days',
    value: '18.945',
    timestamp: '1h ago',
    data: activeUsersData,
    color: 'var(--color-violet-500)',
    icon: TrendingUp,
    gradientId: 'usersGradient',
  },
];

export default function AreaChart1() {
  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <div className="@container w-full max-w-6xl">
        {/* Grid of 3 cards */}
        <div className="grid grid-cols-1 @3xl:grid-cols-3 gap-6">
          {businessCards.map((card, i) => {
            const Icon = card.icon;
            return (
              <Card key={i}>
                <CardContent className="space-y-5">
                  {/* Header with icon and title */}
                  <div className="flex items-center gap-2">
                    <Icon className="size-5" style={{ color: card.color }} />
                    <span className="text-base font-semibold">{card.title}</span>
                  </div>

                  <div className="flex items-end gap-2.5 justify-between">
                    {/* Details */}
                    <div className="flex flex-col gap-1">
                      {/* Period */}
                      <div className="text-sm text-muted-foreground whitespace-nowrap">{card.period}</div>

                      {/* Value */}
                      <div className="text-3xl font-bold text-foreground tracking-tight">{card.value}</div>
                    </div>

                    {/* Chart */}
                    <div className="max-w-40 h-16 w-full relative">
                      <ResponsiveContainer width="100%" height="100%">
                        <AreaChart
                          data={card.data}
                          margin={{
                            top: 5,
                            right: 5,
                            left: 5,
                            bottom: 5,
                          }}
                        >
                          <defs>
                            <linearGradient id={card.gradientId} x1="0" y1="0" x2="0" y2="1">
                              <stop offset="0%" stopColor={card.color} stopOpacity={0.3} />
                              <stop offset="100%" stopColor={card.color} stopOpacity={0.05} />
                            </linearGradient>
                            <filter id={`dotShadow${i}`} x="-50%" y="-50%" width="200%" height="200%">
                              <feDropShadow dx="2" dy="2" stdDeviation="3" floodColor="rgba(0,0,0,0.5)" />
                            </filter>
                          </defs>

                          <Tooltip
                            cursor={{ stroke: card.color, strokeWidth: 1, strokeDasharray: '2 2' }}
                            content={({ active, payload }) => {
                              if (active && payload && payload.length) {
                                const value = payload[0].value as number;
                                const formatValue = (val: number) => {
                                  if (card.title === 'Revenue') {
                                    return `${(val / 1000).toFixed(1)}k US$`;
                                  } else if (card.title === 'New Customers') {
                                    return `${(val / 1000).toFixed(1)}k`;
                                  } else {
                                    return `${(val / 1000).toFixed(1)}k`;
                                  }
                                };

                                return (
                                  <div className="bg-background/95 backdrop-blur-sm border border-border shadow-lg rounded-lg p-2 pointer-events-none">
                                    <p className="text-sm font-semibold text-foreground">{formatValue(value)}</p>
                                  </div>
                                );
                              }
                              return null;
                            }}
                          />

                          {/* Area with gradient and enhanced shadow */}
                          <Area
                            type="monotone"
                            dataKey="value"
                            stroke={card.color}
                            fill={`url(#${card.gradientId})`}
                            strokeWidth={2}
                            dot={false}
                            activeDot={{
                              r: 6,
                              fill: card.color,
                              stroke: 'white',
                              strokeWidth: 2,
                              filter: `url(#dotShadow${i})`,
                            }}
                          />
                        </AreaChart>
                      </ResponsiveContainer>
                    </div>
                  </div>
                </CardContent>
              </Card>
            );
          })}
        </div>
      </div>
    </div>
  );
}
area-chart-2
Open in
Loading...
charts/area-charts/area-chart-2.tsx
'use client';

import React, { useState } from 'react';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/registry/default/ui/select';
import { CreditCard, Eye, ShoppingCart, Store, TrendingDown, TrendingUp } from 'lucide-react';
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts';
import { cn } from '@/lib/utils';

// E-commerce conversion funnel data for different periods
const conversionFunnelData = {
  '7d': [
    { period: 'Mon', storeVisits: 2500, productViews: 2100, addToCart: 1400, checkout: 1200 },
    { period: 'Tue', storeVisits: 2800, productViews: 2300, addToCart: 1600, checkout: 1350 },
    { period: 'Wed', storeVisits: 1900, productViews: 1500, addToCart: 950, checkout: 780 },
    { period: 'Thu', storeVisits: 3100, productViews: 2600, addToCart: 1800, checkout: 1500 },
    { period: 'Fri', storeVisits: 2400, productViews: 1900, addToCart: 1200, checkout: 980 },
    { period: 'Sat', storeVisits: 3400, productViews: 2800, addToCart: 1950, checkout: 1620 },
    { period: 'Sun', storeVisits: 2100, productViews: 1700, addToCart: 1100, checkout: 850 },
  ],
  '30d': [
    { period: 'Week 1', storeVisits: 18500, productViews: 15200, addToCart: 10800, checkout: 8900 },
    { period: 'Week 2', storeVisits: 21200, productViews: 17800, addToCart: 12400, checkout: 10200 },
    { period: 'Week 3', storeVisits: 16800, productViews: 13500, addToCart: 8900, checkout: 7200 },
    { period: 'Week 4', storeVisits: 14200, productViews: 11200, addToCart: 7800, checkout: 6100 },
    { period: 'Week 5', storeVisits: 19800, productViews: 16500, addToCart: 11200, checkout: 9400 },
    { period: 'Week 6', storeVisits: 22800, productViews: 19100, addToCart: 13500, checkout: 11200 },
  ],
  '90d': [
    { period: 'Jan', storeVisits: 78000, productViews: 65000, addToCart: 45000, checkout: 37000 },
    { period: 'Feb', storeVisits: 82000, productViews: 68500, addToCart: 48000, checkout: 39500 },
    { period: 'Mar', storeVisits: 69000, productViews: 54000, addToCart: 36000, checkout: 28500 },
    { period: 'Apr', storeVisits: 61000, productViews: 47000, addToCart: 31000, checkout: 24000 },
    { period: 'May', storeVisits: 75000, productViews: 62000, addToCart: 43000, checkout: 35500 },
    { period: 'Jun', storeVisits: 84000, productViews: 71000, addToCart: 49000, checkout: 41000 },
  ],
  '12m': [
    { period: 'Q1', storeVisits: 235000, productViews: 195000, addToCart: 136000, checkout: 112000 },
    { period: 'Q2', storeVisits: 268000, productViews: 223000, addToCart: 156000, checkout: 128000 },
    { period: 'Q3', storeVisits: 198000, productViews: 158000, addToCart: 105000, checkout: 82000 },
    { period: 'Q4', storeVisits: 175000, productViews: 138000, addToCart: 89000, checkout: 68000 },
    { period: 'Q1 24', storeVisits: 251000, productViews: 209000, addToCart: 146000, checkout: 120000 },
    { period: 'Q2 24', storeVisits: 289000, productViews: 241000, addToCart: 168000, checkout: 138000 },
  ],
};

const chartConfig = {
  storeVisits: {
    label: 'Store Visits',
    color: 'var(--color-indigo-400)',
  },
  productViews: {
    label: 'Product Views',
    color: 'var(--color-indigo-500)',
  },
  addToCart: {
    label: 'Add to Cart',
    color: 'var(--color-indigo-600)',
  },
  checkout: {
    label: 'Checkout',
    color: 'var(--color-indigo-700)',
  },
} satisfies ChartConfig;

// Period configuration
const PERIODS = {
  '7d': { key: '7d', label: 'Last 7 days' },
  '30d': { key: '30d', label: 'Last 30 days' },
  '90d': { key: '90d', label: 'Last 90 days' },
  '12m': { key: '12m', label: 'Last 12 months' },
} as const;

type PeriodKey = keyof typeof PERIODS;

// Define stage metrics
const stageMetrics = [
  { key: 'storeVisits', label: 'Store Visits', icon: Store, color: chartConfig.storeVisits.color },
  { key: 'productViews', label: 'Product Views', icon: Eye, color: chartConfig.productViews.color },
  { key: 'addToCart', label: 'Add to Cart', icon: ShoppingCart, color: chartConfig.addToCart.color },
  { key: 'checkout', label: 'Checkout', icon: CreditCard, color: chartConfig.checkout.color },
] as const;

// Custom Tooltip Component
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    dataKey: string;
    value: number;
    color: string;
  }>;
  label?: string;
}

const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
  if (active && payload && payload.length) {
    return (
      <div className="rounded-lg border bg-popover/95 backdrop-blur-sm p-4 shadow-lg min-w-[200px]">
        <div className="text-sm font-semibold text-popover-foreground mb-3.5 pb-2 border-b border-border/50">
          {label}
        </div>
        <div className="space-y-1.5">
          {stageMetrics.map((stage) => {
            const dataPoint = payload.find((p) => p.dataKey === stage.key);
            const value = dataPoint?.value || 0;

            return (
              <div key={stage.key} className="flex items-center justify-between gap-1.5">
                <div className="flex items-center gap-2">
                  <div className="size-2.5 rounded-sm" style={{ backgroundColor: stage.color }} />
                  <span className="text-xs font-medium text-muted-foreground">{stage.label}</span>
                </div>
                <span className="text-sm font-semibold text-popover-foreground">{value.toLocaleString()}</span>
              </div>
            );
          })}
        </div>
      </div>
    );
  }
  return null;
};

export default function AreaChart2() {
  const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('30d');

  // Get data for selected period
  const currentData = conversionFunnelData[selectedPeriod];

  // Calculate current totals for the latest data point
  const latestData = currentData[currentData.length - 1];

  // Calculate percentage changes (simulated based on period)
  const getChangeForMetric = (metric: string) => {
    const changes = {
      '7d': { storeVisits: -16, productViews: 8, addToCart: -12, checkout: 5 },
      '30d': { storeVisits: 23, productViews: -7, addToCart: 15, checkout: -4 },
      '90d': { storeVisits: 12, productViews: 18, addToCart: -8, checkout: 21 },
      '12m': { storeVisits: -5, productViews: 23, addToCart: 32, checkout: -11 },
    };
    return changes[selectedPeriod][metric as keyof (typeof changes)[typeof selectedPeriod]] || 0;
  };

  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="w-full max-w-5xl">
        <CardHeader className="border-0 min-h-auto py-6">
          <CardTitle className="text-lg font-semibold">Conversion Funnel</CardTitle>
          <CardToolbar>
            {/* Period Selector */}
            <Select value={selectedPeriod} onValueChange={(value) => setSelectedPeriod(value as PeriodKey)}>
              <SelectTrigger>
                <SelectValue />
              </SelectTrigger>
              <SelectContent align="end">
                {Object.values(PERIODS).map((period) => (
                  <SelectItem key={period.key} value={period.key}>
                    {period.label}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </CardToolbar>
        </CardHeader>

        <CardContent className="px-2.5">
          {/* Stats Section */}
          <div className="@container px-2.5">
            <div className="grid @3xl:grid-cols-2 @4xl:grid-cols-4 gap-6 mb-10">
              {stageMetrics.map((stage) => {
                const value = latestData[stage.key as keyof typeof latestData] as number;
                const change = getChangeForMetric(stage.key);

                return (
                  <div key={stage.key} className="space-y-1">
                    <div className="flex items-center gap-2.5">
                      <div className="w-0.5 h-12 rounded-full bg-border"></div>
                      <div className="flex flex-col gap-2">
                        <div className="text-sm font-medium text-muted-foreground">{stage.label}</div>
                        <div className="flex items-center gap-2.5">
                          <span className="text-2xl font-semibold leading-none">{value.toLocaleString()}</span>
                          <span
                            className={cn(
                              'inline-flex items-center gap-1 text-xs font-medium',
                              change >= 0 ? 'text-green-500' : 'text-destructive',
                            )}
                          >
                            {change >= 0 ? <TrendingUp className="size-4" /> : <TrendingDown className="size-4" />}{' '}
                            {/* TODO: Add icon */}
                            {Math.abs(change)}%
                          </span>
                        </div>
                      </div>
                    </div>
                  </div>
                );
              })}
            </div>
          </div>

          {/* Chart */}
          <ChartContainer
            config={chartConfig}
            className="h-[400px] w-full [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
          >
            <AreaChart
              accessibilityLayer
              data={currentData}
              margin={{
                top: 10,
                bottom: 10,
                left: 20,
                right: 20,
              }}
            >
              {/* Background pattern for chart area only */}
              <defs>
                {/* Modern Abstract Background Pattern */}
                <pattern id="modernPattern" x="0" y="0" width="32" height="32" patternUnits="userSpaceOnUse">
                  {/* Diagonal grid lines */}
                  <path
                    d="M0,16 L32,16 M16,0 L16,32"
                    stroke="var(--muted-foreground)"
                    strokeWidth="0.5"
                    strokeOpacity="0.03"
                  />
                  <path
                    d="M0,0 L32,32 M0,32 L32,0"
                    stroke="var(--muted-foreground)"
                    strokeWidth="0.3"
                    strokeOpacity="0.02"
                  />

                  {/* Modern geometric elements */}
                  <circle cx="8" cy="8" r="1.5" fill="var(--muted-foreground)" fillOpacity="0.04" />
                  <circle cx="24" cy="24" r="1.5" fill="var(--muted-foreground)" fillOpacity="0.04" />

                  {/* Abstract rounded rectangles */}
                  <rect x="12" y="4" width="8" height="2" rx="1" fill="var(--muted-foreground)" fillOpacity="0.02" />
                  <rect x="4" y="26" width="8" height="2" rx="1" fill="var(--muted-foreground)" fillOpacity="0.02" />
                  <rect x="20" y="12" width="2" height="8" rx="1" fill="var(--muted-foreground)" fillOpacity="0.02" />

                  {/* Minimal dots */}
                  <circle cx="6" cy="20" r="0.5" fill="var(--muted-foreground)" fillOpacity="0.06" />
                  <circle cx="26" cy="10" r="0.5" fill="var(--muted-foreground)" fillOpacity="0.06" />
                  <circle cx="14" cy="28" r="0.5" fill="var(--muted-foreground)" fillOpacity="0.06" />
                </pattern>

                <linearGradient id="fillStoreVisits" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="5%" stopColor="var(--color-storeVisits)" stopOpacity={0.8} />
                  <stop offset="95%" stopColor="var(--color-storeVisits)" stopOpacity={0.1} />
                </linearGradient>
                <linearGradient id="fillProductViews" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="5%" stopColor="var(--color-productViews)" stopOpacity={0.8} />
                  <stop offset="95%" stopColor="var(--color-productViews)" stopOpacity={0.1} />
                </linearGradient>
                <linearGradient id="fillAddToCart" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="5%" stopColor="var(--color-addToCart)" stopOpacity={0.8} />
                  <stop offset="95%" stopColor="var(--color-addToCart)" stopOpacity={0.1} />
                </linearGradient>
                <linearGradient id="fillCheckout" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="5%" stopColor="var(--color-checkout)" stopOpacity={0.8} />
                  <stop offset="95%" stopColor="var(--color-checkout)" stopOpacity={0.1} />
                </linearGradient>
              </defs>

              <CartesianGrid vertical={false} />

              <XAxis
                dataKey="period"
                tickLine={false}
                axisLine={false}
                tickMargin={10}
                tick={{ textAnchor: 'middle', fontSize: 12 }}
                interval={0}
              />

              <YAxis hide />

              <ChartTooltip
                cursor={{
                  strokeDasharray: '4 4',
                  stroke: 'oklch(45.7% 0.24 277.023)',
                  strokeWidth: 1,
                  strokeOpacity: 0.6,
                }}
                content={<CustomTooltip />}
                offset={20}
                position={{ x: undefined, y: undefined }}
              />

              {/* Background Pattern Areas */}
              <Area
                dataKey="storeVisits"
                type="natural"
                fill="url(#modernPattern)"
                fillOpacity={1}
                stroke="transparent"
                stackId="pattern"
                dot={false}
                activeDot={false}
              />
              <Area
                dataKey="productViews"
                type="natural"
                fill="url(#modernPattern)"
                fillOpacity={1}
                stroke="transparent"
                stackId="pattern"
                dot={false}
                activeDot={false}
              />
              <Area
                dataKey="addToCart"
                type="natural"
                fill="url(#modernPattern)"
                fillOpacity={1}
                stroke="transparent"
                stackId="pattern"
                dot={false}
                activeDot={false}
              />
              <Area
                dataKey="checkout"
                type="natural"
                fill="url(#modernPattern)"
                fillOpacity={1}
                stroke="transparent"
                stackId="pattern"
                dot={false}
                activeDot={false}
              />

              {/* Stacked Areas */}
              <Area
                dataKey="checkout"
                type="natural"
                fill="url(#fillCheckout)"
                fillOpacity={0.5}
                stroke="var(--color-checkout)"
                stackId="a"
                dot={false}
                activeDot={{
                  r: 4,
                  fill: 'var(--color-checkout)',
                  stroke: 'white',
                  strokeWidth: 1.5,
                }}
              />
              <Area
                dataKey="addToCart"
                type="natural"
                fill="url(#fillAddToCart)"
                fillOpacity={0.4}
                stroke="var(--color-addToCart)"
                stackId="a"
                dot={false}
                activeDot={{
                  r: 4,
                  fill: 'var(--color-addToCart)',
                  stroke: 'white',
                  strokeWidth: 1.5,
                }}
              />
              <Area
                dataKey="productViews"
                type="natural"
                fill="url(#fillProductViews)"
                fillOpacity={0.3}
                stroke="var(--color-productViews)"
                stackId="a"
                dot={false}
                activeDot={{
                  r: 4,
                  fill: 'var(--color-productViews)',
                  stroke: 'white',
                  strokeWidth: 1.5,
                }}
              />
              <Area
                dataKey="storeVisits"
                type="natural"
                fill="url(#fillStoreVisits)"
                fillOpacity={0.2}
                stroke="var(--color-storeVisits)"
                stackId="a"
                dot={false}
                activeDot={{
                  r: 4,
                  fill: 'var(--color-storeVisits)',
                  stroke: 'white',
                  strokeWidth: 1.5,
                }}
              />
            </AreaChart>
          </ChartContainer>
        </CardContent>
      </Card>
    </div>
  );
}
area-chart-3
Open in
Loading...
charts/area-charts/area-chart-3.tsx
'use client';

import React, { useState } from 'react';
import { Card, CardContent, CardHeader, CardHeading, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { ToggleGroup, ToggleGroupItem } from '@/registry/default/ui/toggle-group/radix';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/registry/default/ui/tooltip/radix';
import { ChartNoAxesCombined, Info, TrendingUp } from 'lucide-react';
import { Area, AreaChart, XAxis } from 'recharts';

// Digital Marketing Impressions data for different periods (in thousands)
const impressionsData = {
  '5D': [
    { period: 'Mon', impressions: 145.2 },
    { period: 'Tue', impressions: 298.7 },
    { period: 'Wed', impressions: 356.4 },
    { period: 'Thu', impressions: 289.1 },
    { period: 'Fri', impressions: 412.8 },
  ],
  '2W': [
    { period: 'W1', impressions: 1245.5 },
    { period: 'W2', impressions: 1687.3 },
    { period: 'W3', impressions: 1354.9 },
    { period: 'W4', impressions: 1892.6 },
    { period: 'W5', impressions: 1456.2 },
    { period: 'W6', impressions: 2134.7 },
  ],
  '1M': [
    { period: 'W1', impressions: 3245.5 },
    { period: 'W2', impressions: 4187.3 },
    { period: 'W3', impressions: 3654.9 },
    { period: 'W4', impressions: 4892.6 },
    { period: 'W5', impressions: 4156.2 },
    { period: 'W6', impressions: 5234.7 },
    { period: 'W7', impressions: 4823.1 },
    { period: 'W8', impressions: 5567.4 },
  ],
  '6M': [
    { period: 'Jan', impressions: 18745.3 },
    { period: 'Feb', impressions: 22187.7 },
    { period: 'Mar', impressions: 19654.2 },
    { period: 'Apr', impressions: 25892.8 },
    { period: 'May', impressions: 23456.6 },
    { period: 'Jun', impressions: 27234.4 },
  ],
};

const chartConfig = {
  impressions: {
    label: 'Impressions',
    color: 'var(--color-violet-500)',
  },
} satisfies ChartConfig;

// Period configuration
const PERIODS = {
  '5D': { key: '5D', label: '5D' },
  '2W': { key: '2W', label: '2W' },
  '1M': { key: '1M', label: '1M' },
  '6M': { key: '6M', label: '6M' },
} as const;

type PeriodKey = keyof typeof PERIODS;

// Custom Tooltip
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    value: number;
  }>;
  label?: string;
}

const CustomTooltip = ({ active, payload }: TooltipProps) => {
  if (active && payload && payload.length) {
    const value = payload[0].value;
    return (
      <div className="bg-zinc-900 text-white px-3 py-2 rounded-lg text-sm font-medium shadow-lg">
        ${(value / 1000).toFixed(1)}M USD
      </div>
    );
  }
  return null;
};

export default function AreaChart3() {
  const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('5D');

  // Get data for selected period
  const currentData = impressionsData[selectedPeriod];

  // Calculate total impressions for the selected period
  const totalImpressions = currentData.reduce((sum, item) => sum + item.impressions, 0);

  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <div className="bg-muted/50 dark:bg-muted shadow-[0_0_4px_0_rgba(0,0,0,0.0.1)] backdrop-blur-xl border border-border/60 rounded-3xl p-2.5 w-full max-w-sm">
        <Card className="rounded-3xl bg-background border-border/50 p-5">
          <CardHeader className="min-h-auto flex-nowrap! p-0 border-b border-border pt-1 pb-6 mb-6">
            <CardHeading className="flex items-center gap-2.5 space-y-0">
              <div className="flex items-center justify-center size-10 rounded-full bg-muted/80">
                <ChartNoAxesCombined className="size-5" />
              </div>
              <div className="flex flex-col justify-center gap-1">
                <CardTitle className="text-base font-semibold text-foreground leading-none">
                  Campaign Analytics
                </CardTitle>
                <p className="text-sm text-muted-foreground">Impressions and engagement</p>
              </div>
            </CardHeading>
            <CardToolbar>
              <Tooltip>
                <TooltipTrigger>
                  <span>
                    <Info className="size-4 fill-muted/60 text-muted-foreground" />
                  </span>
                </TooltipTrigger>
                <TooltipContent>
                  <p>Campaign analytics by period</p>
                </TooltipContent>
              </Tooltip>
            </CardToolbar>
          </CardHeader>
          <CardContent className="p-0 space-y-6">
            {/* Period Selector */}
            <div className="space-y-6">
              {/* Main Metric */}
              <div className="space-y-1">
                <div className="text-3xl font-semibold text-foreground">
                  {totalImpressions >= 1000
                    ? `${(totalImpressions / 1000).toFixed(1)}M`
                    : `${totalImpressions.toFixed(0)}K`}
                </div>
                <div className="flex items-center gap-2 text-sm">
                  <TrendingUp className="size-4 text-emerald-600" />
                  <span className="text-emerald-600 font-medium">+42%</span>
                  <span className="text-gray-600">compared to last {selectedPeriod === '5D' ? 'week' : 'period'}</span>
                </div>
              </div>

              {/* Toggle Group */}
              <ToggleGroup
                type="single"
                value={selectedPeriod}
                onValueChange={(value) => value && setSelectedPeriod(value as PeriodKey)}
                variant="outline"
                className="w-full shadow-none!"
              >
                {Object.values(PERIODS).map((period) => (
                  <ToggleGroupItem
                    key={period.key}
                    value={period.key}
                    className="flex-1 shadow-none data-[state=on]:bg-muted/60"
                  >
                    {period.label}
                  </ToggleGroupItem>
                ))}
              </ToggleGroup>
            </div>

            {/* Chart */}
            <div className="h-40 w-full">
              <ChartContainer
                config={chartConfig}
                className="h-full w-full rounded-b-3xl overflow-hidden [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
              >
                <AreaChart
                  data={currentData}
                  margin={{
                    top: 10,
                    left: 0,
                    right: 0,
                    bottom: 0,
                  }}
                >
                  <defs>
                    <linearGradient id="impressionsGradient" x1="0" y1="0" x2="0" y2="1">
                      <stop offset="5%" stopColor={chartConfig.impressions.color} stopOpacity={0.8} />
                      <stop offset="95%" stopColor={chartConfig.impressions.color} stopOpacity={0.1} />
                    </linearGradient>

                    <filter id="activeDotShadow" x="-50%" y="-50%" width="200%" height="200%">
                      <feDropShadow
                        dx="2"
                        dy="2"
                        stdDeviation="4"
                        floodColor={chartConfig.impressions.color}
                        floodOpacity="0.6"
                      />
                    </filter>
                  </defs>

                  <XAxis dataKey="period" hide />

                  <ChartTooltip
                    content={<CustomTooltip />}
                    cursor={{
                      strokeWidth: 1,
                      strokeDasharray: '2 2',
                      stroke: chartConfig.impressions.color,
                      strokeOpacity: 1,
                    }}
                  />

                  <Area
                    dataKey="impressions"
                    type="natural"
                    fill="url(#impressionsGradient)"
                    stroke={chartConfig.impressions.color}
                    strokeWidth={2}
                    dot={{
                      r: 4,
                      fill: chartConfig.impressions.color,
                      stroke: 'white',
                      strokeWidth: 2,
                      filter: 'url(#activeDotShadow)',
                    }}
                    activeDot={{
                      r: 6,
                      fill: chartConfig.impressions.color,
                      stroke: 'white',
                      strokeWidth: 2,
                      filter: 'url(#activeDotShadow)',
                    }}
                  />
                </AreaChart>
              </ChartContainer>
            </div>
          </CardContent>
        </Card>
      </div>
    </div>
  );
}
area-chart-4
Open in
Loading...
charts/area-charts/area-chart-4.tsx
'use client';

import React, { Fragment, useState } from 'react';
import { Card, CardContent, CardHeader, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { ToggleGroup, ToggleGroupItem } from '@/registry/default/ui/toggle-group';
import { CheckCircle, Clock, TrendingUp } from 'lucide-react';
import { Area, AreaChart, XAxis, YAxis } from 'recharts';

// Subscription revenue data for different periods
const revenueData = {
  day: [
    { period: '00:00', revenue: 1200 },
    { period: '04:00', revenue: 800 },
    { period: '08:00', revenue: 2100 },
    { period: '12:00', revenue: 3200 },
    { period: '16:00', revenue: 2800 },
    { period: '20:00', revenue: 1900 },
  ],
  week: [
    { period: 'Mon', revenue: 2400 },
    { period: 'Tue', revenue: 2800 },
    { period: 'Wed', revenue: 2200 },
    { period: 'Thu', revenue: 3200 },
    { period: 'Fri', revenue: 2900 },
    { period: 'Sat', revenue: 1800 },
    { period: 'Sun', revenue: 2600 },
  ],
  month: [
    { period: 'Week 1', revenue: 16800 },
    { period: 'Week 2', revenue: 18200 },
    { period: 'Week 3', revenue: 15600 },
    { period: 'Week 4', revenue: 19400 },
  ],
  year: [
    { period: 'Q1', revenue: 198000 },
    { period: 'Q2', revenue: 225000 },
    { period: 'Q3', revenue: 189000 },
    { period: 'Q4', revenue: 267000 },
  ],
};

const chartConfig = {
  revenue: {
    label: 'Revenue',
    color: 'var(--color-slate-600)',
  },
} satisfies ChartConfig;

// Custom Tooltip
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    dataKey: string;
    value: number;
    color: string;
  }>;
  label?: string;
}

const CustomTooltip = ({ active, payload }: TooltipProps) => {
  if (active && payload && payload.length) {
    return (
      <div className="rounded-lg bg-zinc-900 text-white p-3 shadow-lg">
        <div className="text-xs font-medium mb-1">Revenue:</div>
        <div className="text-sm font-semibold">${payload[0].value.toLocaleString()}</div>
      </div>
    );
  }
  return null;
};

// Period configuration
const PERIODS = {
  day: { key: 'day', label: 'Day' },
  week: { key: 'week', label: 'Week' },
  month: { key: 'month', label: 'Month' },
  year: { key: 'year', label: 'Year' },
} as const;

type PeriodKey = keyof typeof PERIODS;

// Statistics data
const statisticsData = [
  {
    id: 'finished',
    label: 'Finished',
    value: '18',
    change: '+4 tasks',
    changeType: 'positive',
    icon: CheckCircle,
  },
  {
    id: 'tracked',
    label: 'Tracked',
    value: '31h',
    change: '-6 hours',
    changeType: 'negative',
    icon: Clock,
  },
  {
    id: 'efficiency',
    label: 'Efficiency',
    value: '93%',
    change: '+12%',
    changeType: 'positive',
    icon: TrendingUp,
  },
] as const;

export default function AreaChart4() {
  const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('day');

  // Get data for selected period
  const currentData = revenueData[selectedPeriod];

  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="w-full lg:max-w-4xl rounded-2xl">
        <CardHeader className="min-h-auto py-6 border-0">
          <CardTitle className="text-xl font-semibold">Orders Overview</CardTitle>
          <CardToolbar>
            <ToggleGroup
              type="single"
              value={selectedPeriod}
              variant="outline"
              onValueChange={(value) => value && setSelectedPeriod(value as PeriodKey)}
              className=""
            >
              {Object.values(PERIODS).map((period) => (
                <ToggleGroupItem
                  key={period.key}
                  value={period.key}
                  className="px-3.5 first:rounded-s-full! last:rounded-e-full!"
                >
                  {period.label}
                </ToggleGroupItem>
              ))}
            </ToggleGroup>
          </CardToolbar>
        </CardHeader>

        <CardContent className="px-0">
          {/* Statistics Blocks */}
          <div className="flex items-center flex-wrap px-6 gap-10 mb-10">
            {statisticsData.map((stat) => {
              const IconComponent = stat.icon;
              return (
                <Fragment key={stat.id}>
                  <div className="h-10 w-px bg-border hidden lg:block first:hidden" />
                  <div key={stat.id} className="flex items-center gap-3">
                    <div className="flex items-center">
                      <div className="flex items-center gap-3">
                        <div className="flex items-center justify-center w-10 h-10 rounded-full bg-muted/60 border border-muted-foreground/10">
                          <IconComponent className="w-4.5 text-muted-foreground" />
                        </div>
                        <div>
                          <div className="text-sm text-muted-foreground mb-0.5">{stat.label}</div>
                          <div className="flex items-center gap-2">
                            <span className="text-2xl font-bold">{stat.value}</span>
                            <span
                              className={`text-sm font-medium ${
                                stat.changeType === 'positive' ? 'text-emerald-600' : 'text-red-600'
                              }`}
                            >
                              {stat.change}
                            </span>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </Fragment>
              );
            })}
          </div>

          {/* Chart */}
          <div className="px-3.5 h-[300px] w-full">
            <ChartContainer
              config={chartConfig}
              className="h-full w-full overflow-visible [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
            >
              <AreaChart
                data={currentData}
                margin={{
                  top: 15,
                  right: 10,
                  left: 10,
                  bottom: 15,
                }}
                style={{ overflow: 'visible' }}
              >
                {/* SVG Pattern for chart area */}
                <defs>
                  {/* Grid pattern */}
                  <pattern id="gridPattern" x="0" y="0" width="20" height="40" patternUnits="userSpaceOnUse">
                    <path d="M 20 0 L 0 0 0 20" fill="none" stroke="var(--input)" strokeWidth="0.5" strokeOpacity="1" />
                  </pattern>

                  {/* Area gradient fill */}
                  <linearGradient id="areaGradient" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stopColor={chartConfig.revenue.color} stopOpacity={0.3} />
                    <stop offset="100%" stopColor={chartConfig.revenue.color} stopOpacity={0.05} />
                  </linearGradient>

                  {/* Shadow filters for dots */}
                  <filter id="dotShadow" x="-100%" y="-100%" width="300%" height="300%">
                    <feDropShadow dx="2" dy="2" stdDeviation="3" floodColor="rgba(0,0,0,0.4)" />
                  </filter>
                  <filter id="activeDotShadow" x="-100%" y="-100%" width="300%" height="300%">
                    <feDropShadow dx="3" dy="3" stdDeviation="4" floodColor="rgba(0,0,0,0.5)" />
                  </filter>
                </defs>

                {/* Background pattern for chart area only */}
                <rect
                  x="60px"
                  y="-20px"
                  width="calc(100% - 75px)"
                  height="calc(100% - 10px)"
                  fill="url(#gridPattern)"
                  style={{ pointerEvents: 'none' }}
                />

                <XAxis
                  dataKey="period"
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 12, fill: 'var(--muted-foreground)' }}
                  tickMargin={8}
                  interval={0}
                  includeHidden={true}
                />

                <YAxis
                  hide={true}
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }}
                  tickFormatter={(value) => `$${value >= 1000 ? `${(value / 1000).toFixed(0)}K` : value}`}
                  tickMargin={8}
                  domain={[0, 'dataMax']}
                  ticks={[0]}
                />

                <ChartTooltip
                  content={<CustomTooltip />}
                  cursor={{
                    stroke: chartConfig.revenue.color,
                    strokeWidth: 1,
                    strokeDasharray: 'none',
                  }}
                />

                <Area
                  type="monotone"
                  dataKey="revenue"
                  stroke={chartConfig.revenue.color}
                  strokeWidth={2}
                  fill="url(#areaGradient)"
                  dot={(props) => {
                    const { cx, cy, payload } = props;
                    // Show dots only for specific periods based on selected time range
                    const showDot =
                      (selectedPeriod === 'day' && (payload.period === '08:00' || payload.period === '16:00')) ||
                      (selectedPeriod === 'week' && (payload.period === 'Thu' || payload.period === 'Sat')) ||
                      (selectedPeriod === 'month' && payload.period === 'Week 2') ||
                      (selectedPeriod === 'year' && payload.period === 'Q2');

                    if (showDot) {
                      return (
                        <circle
                          key={`dot-${cx}-${cy}`}
                          cx={cx}
                          cy={cy}
                          r={4}
                          fill={chartConfig.revenue.color}
                          stroke="white"
                          strokeWidth={2}
                          filter="url(#dotShadow)"
                        />
                      );
                    }
                    return <g key={`dot-${cx}-${cy}`} />; // Return empty group for other points
                  }}
                  activeDot={{
                    r: 6,
                    fill: chartConfig.revenue.color,
                    stroke: 'white',
                    strokeWidth: 2,
                    filter: 'url(#dotShadow)',
                  }}
                />
              </AreaChart>
            </ChartContainer>
          </div>
        </CardContent>
      </Card>
    </div>
  );
}
area-chart-5
Open in
Loading...
charts/area-charts/area-chart-5.tsx
'use client';

import React, { useState } from 'react';
import { Card, CardContent, CardHeader, CardHeading, CardTitle, CardToolbar } from '@/registry/default/ui/card';
import { ChartConfig, ChartContainer, ChartTooltip } from '@/registry/default/ui/chart';
import { ToggleGroup, ToggleGroupItem } from '@/registry/default/ui/toggle-group';
import { Area, ComposedChart, Line, XAxis, YAxis } from 'recharts';

// DeFi protocol financial data
const financeData = {
  day: [
    { month: '00:00', totalDeposits: 4.2, totalBorrowed: 3.1 },
    { month: '04:00', totalDeposits: 4.3, totalBorrowed: 3.2 },
    { month: '08:00', totalDeposits: 4.5, totalBorrowed: 3.3 },
    { month: '12:00', totalDeposits: 4.7, totalBorrowed: 3.3 },
    { month: '16:00', totalDeposits: 4.6, totalBorrowed: 3.2 },
    { month: '20:00', totalDeposits: 4.4, totalBorrowed: 3.1 },
  ],
  week: [
    { month: 'Mon', totalDeposits: 4.2, totalBorrowed: 3.1 },
    { month: 'Tue', totalDeposits: 4.3, totalBorrowed: 3.2 },
    { month: 'Wed', totalDeposits: 4.5, totalBorrowed: 3.3 },
    { month: 'Thu', totalDeposits: 4.7, totalBorrowed: 3.3 },
    { month: 'Fri', totalDeposits: 4.6, totalBorrowed: 3.2 },
    { month: 'Sat', totalDeposits: 4.4, totalBorrowed: 3.1 },
    { month: 'Sun', totalDeposits: 4.3, totalBorrowed: 3.0 },
  ],
  month: [
    { month: 'Jun', totalDeposits: 4.2, totalBorrowed: 3.1 },
    { month: 'Jul', totalDeposits: 4.0, totalBorrowed: 2.9 },
    { month: 'Aug', totalDeposits: 4.1, totalBorrowed: 3.0 },
    { month: 'Sep', totalDeposits: 4.3, totalBorrowed: 3.1 },
    { month: 'Oct', totalDeposits: 4.5, totalBorrowed: 3.2 },
    { month: 'Nov', totalDeposits: 4.7, totalBorrowed: 3.3 },
    { month: 'Dec', totalDeposits: 4.6, totalBorrowed: 3.2 },
    { month: 'Jan', totalDeposits: 4.4, totalBorrowed: 3.1 },
    { month: 'Feb', totalDeposits: 4.3, totalBorrowed: 3.0 },
    { month: 'Mar', totalDeposits: 4.5, totalBorrowed: 3.2 },
    { month: 'Apr', totalDeposits: 4.8, totalBorrowed: 3.4 },
    { month: 'May', totalDeposits: 4.7, totalBorrowed: 3.3 },
  ],
};

const chartConfig = {
  totalDeposits: {
    label: 'Total Deposits',
    color: 'hsl(264, 82%, 70%)',
  },
  totalBorrowed: {
    label: 'Total Borrowed',
    color: 'hsl(172, 82%, 60%)',
  },
} satisfies ChartConfig;

// Custom Tooltip
interface TooltipProps {
  active?: boolean;
  payload?: Array<{
    dataKey: string;
    value: number;
    color: string;
  }>;
  label?: string;
}

const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
  if (active && payload && payload.length) {
    // Filter to unique dataKeys to avoid duplicates from Area + Line components
    const uniquePayload = payload.filter(
      (entry, index, self) => index === self.findIndex((item) => item.dataKey === entry.dataKey),
    );

    return (
      <div className="rounded-lg bg-zinc-800 border border-zinc-700 text-white p-3 shadow-lg">
        <div className="text-xs text-zinc-400 mb-2">{label}</div>
        {uniquePayload.map((entry, index) => (
          <div key={index} className="flex items-center gap-2 mb-1">
            <div className="w-2 h-2 rounded-full" style={{ backgroundColor: entry.color }} />
            <span className="text-sm text-zinc-300">
              {entry.dataKey === 'totalDeposits' ? 'Total Deposits' : 'Total Borrowed'}:
            </span>
            <span className="font-semibold">${entry.value.toFixed(2)}M</span>
          </div>
        ))}
      </div>
    );
  }
  return null;
};

// Period configuration
const PERIODS = {
  day: { key: 'day', label: 'Day' },
  week: { key: 'week', label: 'Week' },
  month: { key: 'month', label: 'Month' },
} as const;

type PeriodKey = keyof typeof PERIODS;

export default function AreaChart5() {
  const [selectedPeriod, setSelectedPeriod] = useState<PeriodKey>('month');

  // Get data for selected period
  const currentData = financeData[selectedPeriod];

  // Calculate total values
  const latestData = currentData[currentData.length - 1];
  const totalValueLocked = latestData.totalDeposits + latestData.totalBorrowed;

  return (
    <div className="min-h-screen flex items-center justify-center p-6 lg:p-8">
      <Card className="w-full rounded-3xl lg:max-w-4xl bg-zinc-950 border-zinc-800 text-white">
        <CardHeader className="min-h-auto gap-5 p-8 border-0">
          <CardHeading className="flex flex-wrap items-end gap-5">
            <div className="min-w-40 space-y-0.5 me-2.5">
              <div className="text-sm text-zinc-400 mb-1">Total Value Locked</div>
              <div className="text-3xl leading-none font-bold">${(totalValueLocked * 1000).toLocaleString()}.15</div>
            </div>
            <div className="flex items-center flex-wrap gap-2.5 mb-1.5">
              <div className="space-y-0.5 pe-10">
                <div
                  className="text-[11px] font-normal flex items-center gap-1.5"
                  style={{ color: chartConfig.totalDeposits.color }}
                >
                  <div
                    className="size-1.5 rounded-full "
                    style={{ backgroundColor: chartConfig.totalDeposits.color }}
                  />
                  Total Deposits
                </div>
                <div className="text-xl font-bold leading-none">
                  ${(latestData.totalDeposits * 1000).toLocaleString()}.43
                </div>
              </div>

              <div className="space-y-0.5">
                <div
                  className="text-[11px] font-normal flex items-center gap-1.5"
                  style={{ color: chartConfig.totalBorrowed.color }}
                >
                  <div
                    className="size-1.5 rounded-full "
                    style={{ backgroundColor: chartConfig.totalBorrowed.color }}
                  />
                  Total Borrowed
                </div>
                <div className="text-xl font-bold leading-none">
                  ${(latestData.totalBorrowed * 1000).toLocaleString()}.15
                </div>
              </div>
            </div>
          </CardHeading>
          <CardToolbar>
            <ToggleGroup
              type="single"
              value={selectedPeriod}
              onValueChange={(value) => value && setSelectedPeriod(value as PeriodKey)}
              className="bg-zinc-800 p-1 rounded-full"
            >
              {Object.values(PERIODS).map((period) => (
                <ToggleGroupItem
                  key={period.key}
                  value={period.key}
                  className="px-4 py-2 text-sm data-[state=on]:bg-zinc-700 data-[state=on]:text-white text-zinc-400 hover:bg-zinc-700 hover:text-white rounded-full"
                >
                  {period.label}
                </ToggleGroupItem>
              ))}
            </ToggleGroup>
          </CardToolbar>
        </CardHeader>

        <CardContent className="ps-2.5 pe-4.5">
          <div className="h-[400px] w-full">
            <ChartContainer
              config={chartConfig}
              className="h-full w-full overflow-visible [&_.recharts-curve.recharts-tooltip-cursor]:stroke-initial"
            >
              <ComposedChart
                data={currentData}
                margin={{
                  top: 25,
                  right: 25,
                  left: 15,
                  bottom: 25,
                }}
                style={{ overflow: 'visible' }}
              >
                <defs>
                  {/* Grid pattern */}
                  <pattern id="gridPattern" x="0" y="0" width="30" height="30" patternUnits="userSpaceOnUse">
                    <path
                      d="M 30 0 L 0 0 0 30"
                      fill="none"
                      stroke="rgb(51 65 85)"
                      strokeWidth="0.5"
                      strokeOpacity="0.3"
                    />
                  </pattern>

                  {/* Linear gradients for areas */}
                  <linearGradient id="depositsAreaGradient" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stopColor={chartConfig.totalDeposits.color} stopOpacity="0.3" />
                    <stop offset="100%" stopColor={chartConfig.totalDeposits.color} stopOpacity="0.02" />
                  </linearGradient>

                  <linearGradient id="borrowedAreaGradient" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stopColor={chartConfig.totalBorrowed.color} stopOpacity="0.3" />
                    <stop offset="100%" stopColor={chartConfig.totalBorrowed.color} stopOpacity="0.02" />
                  </linearGradient>

                  {/* Shadow filters for dots */}
                  <filter id="dotShadow" x="-100%" y="-100%" width="300%" height="300%">
                    <feDropShadow dx="2" dy="2" stdDeviation="3" floodColor="rgba(0,0,0,0.4)" />
                  </filter>
                  <filter id="activeDotShadow" x="-100%" y="-100%" width="300%" height="300%">
                    <feDropShadow dx="3" dy="4" stdDeviation="6" floodColor="rgba(0,0,0,0.6)" />
                  </filter>
                </defs>

                {/* Background grid */}
                <rect
                  x="0"
                  y="0"
                  width="100%"
                  height="100%"
                  fill="url(#gridPattern)"
                  style={{ pointerEvents: 'none' }}
                />

                <XAxis
                  dataKey="month"
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 12, fill: 'rgb(148 163 184)' }}
                  tickMargin={15}
                />

                <YAxis
                  axisLine={false}
                  tickLine={false}
                  tick={{ fontSize: 12, fill: 'rgb(148 163 184)' }}
                  tickFormatter={(value) => `$${value.toFixed(1)}M`}
                  domain={['dataMin - 0.2', 'dataMax + 0.2']}
                  tickMargin={15}
                />

                <ChartTooltip content={<CustomTooltip />} />

                {/* Area fills with gradients */}
                <Area
                  type="monotone"
                  dataKey="totalDeposits"
                  stroke="transparent"
                  fill="url(#depositsAreaGradient)"
                  strokeWidth={0}
                  dot={false}
                />

                <Area
                  type="monotone"
                  dataKey="totalBorrowed"
                  stroke="transparent"
                  fill="url(#borrowedAreaGradient)"
                  strokeWidth={0}
                  dot={false}
                />

                {/* Line strokes on top */}
                <Line
                  type="monotone"
                  dataKey="totalDeposits"
                  stroke={chartConfig.totalDeposits.color}
                  strokeWidth={2}
                  dot={{
                    r: 4,
                    fill: chartConfig.totalDeposits.color,
                    stroke: 'white',
                    strokeWidth: 2,
                    filter: 'url(#dotShadow)',
                  }}
                  activeDot={{
                    r: 6,
                    fill: chartConfig.totalDeposits.color,
                    strokeWidth: 2,
                    stroke: 'white',
                    filter: 'url(#activeDotShadow)',
                  }}
                />

                <Line
                  type="monotone"
                  dataKey="totalBorrowed"
                  stroke={chartConfig.totalBorrowed.color}
                  strokeWidth={2}
                  dot={{
                    r: 4,
                    fill: chartConfig.totalBorrowed.color,
                    stroke: 'white',
                    strokeWidth: 2,
                    filter: 'url(#dotShadow)',
                  }}
                  activeDot={{
                    r: 6,
                    fill: chartConfig.totalBorrowed.color,
                    strokeWidth: 2,
                    stroke: 'white',
                    filter: 'url(#activeDotShadow)',
                  }}
                />
              </ComposedChart>
            </ChartContainer>
          </div>
        </CardContent>
      </Card>
    </div>
  );
}

Didn't find what you were looking for?

Suggest block

We use cookies

We use cookies to ensure you get the best experience on our website. For more information on how we use cookies, please see our cookie policy.

By clicking Accept, you agree to our use of cookies.
Learn more.

Token UI

Components

  • Overview
  • Pricing
  • Marketplace
  • Features
  • Integrations
  • Pricing

Blocks

  • Charts
  • Team
  • Blog
  • Careers
  • Contact
  • Privacy

Examples

  • Help
  • Sales
  • Advertise

Docs

  • Twitter
  • Instagram
  • LinkedIn

© 2025 TOUI.dev. All rights reserved.

  • Terms and Conditions
  • Privacy Policy