React Components
Copy-paste React components for common patterns.
Lead Capture Form
'use client';
import { useState } from 'react';
interface LeadFormProps {
onSuccess?: (lead: any) => void;
}
export function LeadForm({ onSuccess }: LeadFormProps) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setLoading(true);
setError('');
const form = e.currentTarget;
const formData = new FormData(form);
try {
const res = await fetch('/api/leads', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(Object.fromEntries(formData)),
});
if (!res.ok) throw new Error('Failed to submit');
const lead = await res.json();
onSuccess?.(lead);
form.reset();
} catch (err) {
setError('Something went wrong. Please try again.');
} finally {
setLoading(false);
}
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<input
name="name"
placeholder="Your Name"
required
className="w-full px-4 py-3 rounded-lg border"
/>
<input
name="email"
type="email"
placeholder="Email"
required
className="w-full px-4 py-3 rounded-lg border"
/>
<input
name="phone"
type="tel"
placeholder="Phone"
required
className="w-full px-4 py-3 rounded-lg border"
/>
{error && <p className="text-red-500 text-sm">{error}</p>}
<button
type="submit"
disabled={loading}
className="w-full py-3 bg-orange-500 text-white rounded-lg font-bold"
>
{loading ? 'Submitting...' : 'Get My Cash Offer'}
</button>
</form>
);
}
Metric Card
interface MetricCardProps {
title: string;
value: string | number;
change?: number;
icon?: string;
}
export function MetricCard({ title, value, change, icon }: MetricCardProps) {
return (
<div className="p-6 bg-slate-800 rounded-xl">
<div className="flex items-center justify-between mb-2">
<span className="text-slate-400 text-sm">{title}</span>
{icon && <span className="text-2xl">{icon}</span>}
</div>
<div className="text-3xl font-bold">{value}</div>
{change !== undefined && (
<div className={text-sm mt-1 ${change >= 0 ? 'text-green-500' : 'text-red-500'}}>
{change >= 0 ? '↑' : '↓'} {Math.abs(change)}%
</div>
)}
</div>
);
}