Vector Motion
Finance

Customer Acquisition Cost Card - Marketing ROI

Monitor customer acquisition costs and CAC trends. Track marketing spend efficiency and ROI.

Finance CAC Card

The Finance CAC Card displays Customer Acquisition Cost metrics, breaking down marketing and sales spend to help teams understand the cost of acquiring new customers and optimize their acquisition strategies.

Preview

Installation

npx shadcn@latest add https://vectormotion.vercel.app/registry/finance-cac-card.json
Finance CAC Card
'use client'import React from 'react';import { Users, TrendingDown } from 'lucide-react';import { motion } from 'motion/react';import { clsx, type ClassValue } from "clsx"import { twMerge } from "tailwind-merge"import { ResponsiveContainer, BarChart, Bar, Tooltip, XAxis, YAxis } from 'recharts';function cn(...inputs: ClassValue[]) {  return twMerge(clsx(inputs))}interface CACData {  source: string;  marketing: number;  sales: number;}interface CACCardProps {  isInteractive?: boolean;  className?: string;  title?: string;  value?: string;  trend?: string;  target?: string;  data?: CACData[];}const DEFAULT_TITLE = "CAC";const DEFAULT_VALUE = "$345";const DEFAULT_TREND = "-5%";const DEFAULT_TARGET = "$400";const DEFAULT_DATA: CACData[] = [  { source: 'Paid Ads', marketing: 120, sales: 50 },  { source: 'Organic', marketing: 20, sales: 30 },  { source: 'Ref', marketing: 40, sales: 20 },];export const CACCard: React.FC<CACCardProps> = ({  isInteractive = true,  className = "",  title = DEFAULT_TITLE,  value = DEFAULT_VALUE,  trend = DEFAULT_TREND,  target = DEFAULT_TARGET,  data = DEFAULT_DATA,}) => {  const index = 27;  return (    <motion.div      layoutId={isInteractive ? `card-${index}-${title}` : undefined}      transition={{ duration: 0.4, ease: "easeOut" }}      className={cn(        "relative overflow-hidden rounded-xl border border-border bg-card text-card-foreground p-5 shadow-sm transition-all flex flex-col h-full group",        isInteractive ? "cursor-pointer hover:border-primary/50 hover:shadow-md" : "",        className      )}    >      <div className="mb-2 flex items-start justify-between relative z-10">        <div>          <h3 className="font-semibold text-lg text-foreground">            {title}          </h3>          <div className="flex items-center gap-2 mt-1">            <span className="text-2xl font-bold text-foreground">{value}</span>            <span className="text-xs bg-emerald-500/10 text-emerald-600 px-1.5 py-0.5 rounded-full flex items-center gap-0.5">              <TrendingDown className="w-3 h-3" /> {trend}            </span>          </div>        </div>        <div className="rounded-lg bg-emerald-500/10 p-2 text-emerald-500">          <Users className="h-5 w-5" />        </div>      </div>      <div className="relative z-10 flex-1 min-h-[140px]">        <div className="absolute inset-0">          <ResponsiveContainer width="100%" height="100%">            <BarChart layout="vertical" data={data} barGap={2} barSize={16}>              <XAxis type="number" hide />              <YAxis dataKey="source" type="category" width={60} tick={{ fontSize: 10, fill: '#71717a' }} axisLine={false} tickLine={false} />              <Tooltip cursor={{ fill: 'transparent' }} contentStyle={{ fontSize: '12px' }} />              <Bar dataKey="marketing" stackId="a" fill="#10b981" radius={[0, 4, 4, 0]} name="Marketing" />              <Bar dataKey="sales" stackId="a" fill="#3b82f6" radius={[0, 4, 4, 0]} name="Sales" />            </BarChart>          </ResponsiveContainer>        </div>      </div>      <div className="z-10 mt-2 flex items-center justify-between text-xs pt-2 border-t border-border">        <div className="flex gap-2">          <span className="flex items-center gap-1"><div className="w-2 h-2 rounded-full bg-emerald-500" /> Marketing</span>          <span className="flex items-center gap-1"><div className="w-2 h-2 rounded-full bg-blue-500" /> Sales</span>        </div>        <span className="text-muted-foreground">Target: {target}</span>      </div>    </motion.div>  );};

Props

Prop

Type