line-chart-1
Single block view - Copy and use this block in your project: line-chart-1
Loading...
charts/line-charts/line-chart-1.tsx
"use client";
import React from "react";
import { Badge } from "@/registry/default/ui/badge/radix";
import { Button } from "@/registry/default/ui/button/radix";
import {
Card,
CardContent,
CardHeader,
CardTitle,
CardToolbar,
} from "@/registry/default/ui/card/radix";
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>
);
}
Component Data
- Name:
- Line Chart 1 – B2B Sales Goals vs Performance
- Access:
- Pro
- Created:
- 2026-01-03
- Updated:
- 2026-01-04
- Released:
- N/A
- Type:
- Block
line-chartchartdashboardanalyticssalesb2brechartstailwindreact
Line Chart 1 – Sales Goals vs Performance
A sophisticated dual-metric line chart designed for B2B sales analytics. Visualizes actual sales performance against target goals with gradient fills, reference lines, and interactive tooltips.
Key Features
- Dual Data Series – Compare goals (pink) and actual sales (teal) on one chart.
- Gradient Area Fill – Semi-transparent teal gradient under the sales line.
- Custom Tooltips – Detailed hover information with percentage comparison badges.
- Reference Line – Target line indicator for quick goal assessment.
- Dropdown Actions – Export, share, filter, and refresh options in header menu.
Visual Design
- Color Scheme – Pink-500 for goals, Teal-500 for sales.
- Legend Display – Color-coded labels with bordered circles.
- Responsive Container – Adapts to card width automatically.
Use Cases
- Sales Dashboards – Track monthly/quarterly sales vs targets.
- Revenue Analytics – Monitor SaaS MRR against projections.
- Performance Reviews – Visualize team goals vs achievements.
- Executive Reports – Present KPIs to stakeholders.
Technical Stack
- Recharts – ComposedChart with Area, Line, and ReferenceLine.
- Tailwind CSS – Utility styling with CSS variables for colors.
- Radix UI – Card, Badge, Button, and DropdownMenu components.
- Lucide Icons – ArrowUp, ArrowDown, MoreHorizontal, etc.
Didn't find what you were looking for?
Suggest block