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.
line-chart-1
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
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
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
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
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
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
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
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
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'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.
area-chart-1
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
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
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
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
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