Before / After Comparison
Toggle-based before/after comparison block showing client transformation across multiple dimensions.
Preview
Source
tsx
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { ArrowRight, CheckCircle, XCircle } from "lucide-react";
const comparisons = [
{
id: 1,
label: "Website Performance",
before: {
headline: "Slow, outdated site",
description: "3.8s load time, no mobile optimization, built on an aging CMS that required a developer for every change.",
stats: "12% conversion rate · 72% bounce rate",
},
after: {
headline: "Fast, modern, self-serve",
description: "0.9s load time, fully responsive, headless CMS with visual editor — team makes changes in minutes, not weeks.",
stats: "34% conversion rate · 38% bounce rate",
},
},
{
id: 2,
label: "Lead Generation",
before: {
headline: "Random, inconsistent leads",
description: "No defined funnel, relying on word-of-mouth, no email capture, no nurture sequences, sporadic follow-up.",
stats: "~8 leads/month · $420 avg cost per lead",
},
after: {
headline: "Predictable pipeline",
description: "Multi-channel funnel with content-gated offers, automated email sequences, and a CRM synced to every touchpoint.",
stats: "~47 leads/month · $68 avg cost per lead",
},
},
{
id: 3,
label: "Brand Positioning",
before: {
headline: "Generic and forgettable",
description: "Logo from a stock site, inconsistent colors across pages, messaging that sounded like every competitor.",
stats: "2% brand recall · 0 unsolicited referrals",
},
after: {
headline: "Distinct and memorable",
description: "Custom identity system with a clear visual language, sharp messaging framework, and a brand guide the team actually uses.",
stats: "61% brand recall · 14 referrals in first quarter",
},
},
];
export default function BeforeAfter() {
const [view, setView] = useState<"before" | "after">("before");
return (
<section className="bg-background py-16 px-4 sm:px-6 lg:px-8">
<div className="mx-auto max-w-4xl">
{/* Header */}
<div className="text-center mb-10">
<p className="text-sm font-semibold uppercase tracking-widest text-primary mb-2">
The Transformation
</p>
<h2 className="text-3xl font-bold text-foreground sm:text-4xl mb-4">
Before vs. After
</h2>
<p className="text-muted-foreground max-w-xl mx-auto text-base">
See exactly what changed — and why it matters.
</p>
</div>
{/* Toggle */}
<div className="flex justify-center mb-10">
<div className="inline-flex rounded-full border border-border bg-muted p-1 gap-1">
<button
onClick={() => setView("before")}
className={`rounded-full px-6 py-2 text-sm font-semibold transition-all duration-200 ${
view === "before"
? "bg-red-500 text-white shadow"
: "text-muted-foreground hover:text-foreground"
}`}
>
Before
</button>
<button
onClick={() => setView("after")}
className={`rounded-full px-6 py-2 text-sm font-semibold transition-all duration-200 ${
view === "after"
? "bg-green-500 text-white shadow"
: "text-muted-foreground hover:text-foreground"
}`}
>
After
</button>
</div>
</div>
{/* Cards */}
<div className="space-y-4">
{comparisons.map((item) => {
const data = view === "before" ? item.before : item.after;
const isBefore = view === "before";
return (
<div
key={item.id}
className={`rounded-2xl border p-6 transition-all duration-300 ${
isBefore
? "border-red-200 bg-red-500/5"
: "border-green-200 bg-green-500/5"
}`}
>
<div className="flex items-start gap-4">
<div
className={`mt-0.5 shrink-0 rounded-full p-1 ${
isBefore ? "text-red-500" : "text-green-500"
}`}
>
{isBefore ? (
<XCircle className="h-5 w-5" />
) : (
<CheckCircle className="h-5 w-5" />
)}
</div>
<div className="flex-1 min-w-0">
<div className="flex flex-wrap items-center gap-2 mb-1">
<span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
{item.label}
</span>
<span
className={`text-xs rounded-full px-2 py-0.5 font-semibold ${
isBefore
? "bg-red-100 text-red-600"
: "bg-green-100 text-green-700"
}`}
>
{isBefore ? "Before" : "After"}
</span>
</div>
<h3
className={`text-lg font-bold mb-1 ${
isBefore ? "text-red-700" : "text-green-800"
}`}
>
{data.headline}
</h3>
<p className="text-sm text-muted-foreground leading-relaxed mb-3">
{data.description}
</p>
<div
className={`inline-block rounded-full px-3 py-1 text-xs font-mono font-medium ${
isBefore
? "bg-red-100 text-red-700"
: "bg-green-100 text-green-700"
}`}
>
{data.stats}
</div>
</div>
</div>
</div>
);
})}
</div>
{/* CTA */}
<div className="mt-10 flex justify-center">
<Button size="lg" className="gap-2">
Get these results for your business
<ArrowRight className="h-4 w-4" />
</Button>
</div>
</div>
</section>
);
} Claude Code Instructions
CLI Install
npx innovations add before-afterWhere to use it
Use this on case study pages or your services page to show the contrast between client's situation before and after working with you.
In Astro:
import BeforeAfter from '../components/innovations/case-studies/before-after';
<BeforeAfter client:visible />
In Next.js:
import BeforeAfter from '@/components/innovations/case-studies/before-after';
Replace the comparisons array with your actual before/after data. Each row needs: label, before { headline, description, stats }, after { headline, description, stats }.