This document provides detailed usage examples and patterns for all components in the Betirement website.
The component library is organized into categories:
- UI Components: Basic building blocks
- Layout Components: Page structure
- Section Components: Page sections
- Form Components: Forms and inputs
- Content Components: Content display
- Analytics Components: Tracking and monitoring
- Social Components: Social media integration
Versatile button component with multiple variants and sizes.
Location: src/components/ui/Button.tsx
Props:
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
onClick?: () => void;
href?: string;
disabled?: boolean;
loading?: boolean;
className?: string;
}Usage:
import { Button } from '@/components/ui/Button';
// Primary button
<Button variant="primary" size="md">
Subscribe Now
</Button>
// Link button
<Button variant="outline" href="/about">
Learn More
</Button>
// Loading state
<Button variant="primary" loading={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</Button>
// Disabled button
<Button variant="secondary" disabled>
Coming Soon
</Button>Variants:
primary: Bitcoin orange background, white textsecondary: Black background, white textoutline: Transparent with borderghost: Transparent, no border
Container component for content grouping.
Location: src/components/ui/Card.tsx
Props:
interface CardProps {
title?: string;
description?: string;
image?: string;
href?: string;
badge?: string;
footer?: React.ReactNode;
children?: React.ReactNode;
className?: string;
}Usage:
import { Card } from '@/components/ui/Card';
// Basic card
<Card
title="Bitcoin Fundamentals"
description="Learn the basics of Bitcoin investing"
image="/images/bitcoin-basics.jpg"
/>
// Interactive card
<Card
title="Retirement Calculator"
href="/resources/calculator"
badge="Popular"
footer={<Button variant="outline">Try Now</Button>}
>
<p>Calculate your retirement timeline</p>
</Card>
// Custom content
<Card>
<h3>Custom Content</h3>
<p>Any content you want</p>
</Card>Accessible modal dialog component.
Location: src/components/ui/Modal.tsx
Props:
interface ModalProps {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
size?: 'sm' | 'md' | 'lg' | 'xl';
showCloseButton?: boolean;
}Usage:
import { Modal } from '@/components/ui/Modal';
import { useState } from 'react';
function MyComponent() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button onClick={() => setIsOpen(true)}>
Open Modal
</Button>
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Subscribe to Newsletter"
size="md"
>
<EmailCaptureForm onSuccess={() => setIsOpen(false)} />
</Modal>
</>
);
}Features:
- Keyboard navigation (ESC to close)
- Focus trap
- Backdrop click to close
- Accessible (ARIA labels)
- Responsive sizing
Form input component with validation states.
Location: src/components/ui/Input.tsx
Props:
interface InputProps {
type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url';
label?: string;
placeholder?: string;
value: string;
onChange: (value: string) => void;
error?: string;
required?: boolean;
disabled?: boolean;
className?: string;
}Usage:
import { Input } from '@/components/ui/Input';
import { useState } from 'react';
function MyForm() {
const [email, setEmail] = useState('');
const [error, setError] = useState('');
const handleChange = (value: string) => {
setEmail(value);
if (error) setError('');
};
return (
<Input
type="email"
label="Email Address"
placeholder="you@example.com"
value={email}
onChange={handleChange}
error={error}
required
/>
);
}Catches and handles React errors gracefully.
Location: src/components/ui/ErrorBoundary.tsx
Props:
interface ErrorBoundaryProps {
children: React.ReactNode;
fallback?: React.ReactNode;
}Usage:
import { ErrorBoundary } from '@/components/ui/ErrorBoundary';
// Wrap components that might error
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<VideoPlayer videoId={videoId} />
</ErrorBoundary>
// With custom fallback
<ErrorBoundary
fallback={
<Card>
<h3>Unable to load content</h3>
<Button onClick={() => window.location.reload()}>
Reload Page
</Button>
</Card>
}
>
<ComplexComponent />
</ErrorBoundary>Main site header with navigation.
Location: src/components/layout/Header.tsx
Usage:
import { Header } from '@/components/layout/Header';
// In layout.tsx
<Header />Features:
- Sticky positioning
- Mobile hamburger menu
- Desktop dropdown menus
- CTA button
- Logo with link to home
Site footer with links and newsletter signup.
Location: src/components/layout/Footer.tsx
Usage:
import { Footer } from '@/components/layout/Footer';
// In layout.tsx
<Footer />Features:
- Multi-column layout
- Social media links
- Newsletter signup form
- Legal links
- Copyright notice
Navigation menu component.
Location: src/components/layout/Navigation.tsx
Usage:
import { Navigation } from '@/components/layout/Navigation';
<Navigation items={navigationItems} />Homepage hero section.
Location: src/components/sections/HeroSection.tsx
Props:
interface HeroSectionProps {
headline: string;
subheadline?: string;
ctaPrimary?: {
text: string;
href: string;
};
ctaSecondary?: {
text: string;
href: string;
};
backgroundImage?: string;
trustIndicators?: string[];
}Usage:
import { HeroSection } from '@/components/sections/HeroSection';
<HeroSection
headline="Your Bridge to Bitcoin-Powered Freedom"
subheadline="Real experience. Proven strategies. Your path to early retirement."
ctaPrimary={{
text: "Start Here",
href: "/start-here"
}}
ctaSecondary={{
text: "Watch My Story",
href: "/about"
}}
trustIndicators={[
"Retired at 51",
"28 Years Corporate Experience",
"Bitcoin Since 2017"
]}
/>Timeline component with scroll animations.
Location: src/components/sections/InteractiveTimeline.tsx
Props:
interface TimelineEvent {
year: string;
title: string;
description: string;
image?: string;
}
interface InteractiveTimelineProps {
events: TimelineEvent[];
}Usage:
import { InteractiveTimeline } from '@/components/sections/InteractiveTimeline';
const events = [
{
year: "1995",
title: "Started Corporate Career",
description: "Began 28-year journey in corporate America",
image: "/images/timeline/1995.jpg"
},
{
year: "2017",
title: "Discovered Bitcoin",
description: "First Bitcoin purchase and deep dive into crypto",
image: "/images/timeline/2017.jpg"
},
// ... more events
];
<InteractiveTimeline events={events} />Showcase featured content on homepage.
Location: src/components/sections/FeaturedContent.tsx
Usage:
import { FeaturedContent } from '@/components/sections/FeaturedContent';
<FeaturedContent
video={featuredVideo}
blogPost={latestPost}
product={spotlightProduct}
/>Display social proof metrics.
Location: src/components/sections/SocialProofBar.tsx
Usage:
import { SocialProofBar } from '@/components/sections/SocialProofBar';
<SocialProofBar
youtubeSubscribers={125000}
totalViews={5000000}
communityMembers={2500}
/>Email subscription form with ConvertKit integration.
Location: src/components/forms/EmailCaptureForm.tsx
Props:
interface EmailCaptureFormProps {
variant?: 'inline' | 'modal' | 'slide-in';
leadMagnet?: string;
tags?: string[];
source?: string;
onSuccess?: () => void;
onError?: (error: string) => void;
}Usage:
import { EmailCaptureForm } from '@/components/forms/EmailCaptureForm';
// Inline form
<EmailCaptureForm
variant="inline"
tags={['website-home']}
source="homepage-hero"
onSuccess={() => console.log('Subscribed!')}
/>
// Modal form with lead magnet
<EmailCaptureForm
variant="modal"
leadMagnet="Bitcoin Retirement Guide"
tags={['lead-magnet', 'guide']}
source="resource-download"
/>
// Slide-in form
<EmailCaptureForm
variant="slide-in"
tags={['exit-intent']}
source="exit-popup"
/>General contact form with Netlify Forms.
Location: src/components/forms/ContactForm.tsx
Props:
interface ContactFormProps {
type?: 'general' | 'speaking' | 'media';
onSubmit?: (data: FormData) => void;
}Usage:
import { ContactForm } from '@/components/forms/ContactForm';
// General contact
<ContactForm type="general" />
// Speaking request
<ContactForm
type="speaking"
onSubmit={(data) => {
console.log('Form submitted:', data);
}}
/>Form wrapper with validation.
Location: src/components/forms/ValidatedForm.tsx
Usage:
import { ValidatedForm } from '@/components/forms/ValidatedForm';
import { Input } from '@/components/ui/Input';
<ValidatedForm
onSubmit={handleSubmit}
validationSchema={schema}
>
<Input name="email" type="email" required />
<Input name="name" type="text" required />
<Button type="submit">Submit</Button>
</ValidatedForm>Display video information in a card.
Location: src/components/content/VideoCard.tsx
Props:
interface VideoCardProps {
id: string;
title: string;
thumbnail: string;
duration: string;
category: string;
publishedAt: string;
viewCount?: number;
}Usage:
import { VideoCard } from '@/components/content/VideoCard';
<VideoCard
id="abc123"
title="Bitcoin Retirement Strategy"
thumbnail="https://i.ytimg.com/vi/abc123/hqdefault.jpg"
duration="10:30"
category="Bitcoin Fundamentals"
publishedAt="2024-01-01"
viewCount={125000}
/>Display blog post in a card.
Location: src/components/content/BlogCard.tsx
Props:
interface BlogCardProps {
slug: string;
title: string;
excerpt: string;
coverImage: string;
publishedAt: string;
readingTime: number;
tags: string[];
featured?: boolean;
}Usage:
import { BlogCard } from '@/components/content/BlogCard';
<BlogCard
slug="bitcoin-retirement-basics"
title="Bitcoin Retirement Basics"
excerpt="Learn the fundamentals of retiring with Bitcoin"
coverImage="/images/blog/bitcoin-basics.jpg"
publishedAt="2024-01-01"
readingTime={5}
tags={['bitcoin', 'retirement', 'basics']}
featured
/>Display downloadable resource.
Location: src/components/content/ResourceCard.tsx
Props:
interface ResourceCardProps {
title: string;
description: string;
type: 'pdf' | 'calculator' | 'template';
downloadUrl?: string;
requiresEmail: boolean;
difficulty: 'beginner' | 'intermediate' | 'advanced';
}Usage:
import { ResourceCard } from '@/components/content/ResourceCard';
<ResourceCard
title="Bitcoin Allocation Calculator"
description="Calculate optimal Bitcoin allocation for your portfolio"
type="calculator"
downloadUrl="/resources/calculator"
requiresEmail={false}
difficulty="intermediate"
/>Grid layout for videos.
Location: src/components/content/VideoGrid.tsx
Usage:
import { VideoGrid } from '@/components/content/VideoGrid';
<VideoGrid videos={videos} columns={3} />Pagination component for lists.
Location: src/components/content/Pagination.tsx
Props:
interface PaginationProps {
currentPage: number;
totalPages: number;
onPageChange: (page: number) => void;
}Usage:
import { Pagination } from '@/components/content/Pagination';
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={setCurrentPage}
/>Wrap app with analytics tracking.
Location: src/components/analytics/AnalyticsProvider.tsx
Usage:
import { AnalyticsProvider } from '@/components/analytics/AnalyticsProvider';
// In layout.tsx
<AnalyticsProvider>
{children}
</AnalyticsProvider>Monitor and report performance metrics.
Location: src/components/analytics/PerformanceMonitor.tsx
Usage:
import { PerformanceMonitor } from '@/components/analytics/PerformanceMonitor';
// In layout.tsx
<PerformanceMonitor />Display Instagram posts.
Location: src/components/social/InstagramFeed.tsx
Props:
interface InstagramFeedProps {
maxPosts?: number;
}Usage:
import { InstagramFeed } from '@/components/social/InstagramFeed';
<InstagramFeed maxPosts={6} />Social sharing buttons.
Location: src/components/social/SocialShare.tsx
Props:
interface SocialShareProps {
url: string;
title: string;
description?: string;
platforms?: ('twitter' | 'linkedin' | 'facebook' | 'email')[];
}Usage:
import { SocialShare } from '@/components/social/SocialShare';
<SocialShare
url="https://betirement.com/blog/post-slug"
title="Bitcoin Retirement Strategy"
description="Learn how to retire early with Bitcoin"
platforms={['twitter', 'linkedin', 'facebook']}
/>- Single Responsibility: Each component should do one thing well
- Composition: Build complex UIs from simple components
- Props Interface: Always define TypeScript interfaces
- Default Props: Provide sensible defaults
- Error Handling: Handle errors gracefully
- Semantic HTML: Use appropriate HTML elements
- ARIA Labels: Add labels for screen readers
- Keyboard Navigation: Support keyboard users
- Focus Management: Manage focus appropriately
- Color Contrast: Ensure sufficient contrast
- Lazy Loading: Use dynamic imports for heavy components
- Memoization: Use React.memo for expensive renders
- Code Splitting: Split code by route
- Image Optimization: Use Next.js Image component
- Bundle Size: Keep components small
- Unit Tests: Test component logic
- Integration Tests: Test component interactions
- Accessibility Tests: Test with screen readers
- Visual Tests: Test responsive design
- E2E Tests: Test user flows
function MyForm() {
const [formData, setFormData] = useState({
email: '',
name: '',
});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
try {
// Validate
const validationErrors = validate(formData);
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
return;
}
// Submit
await submitForm(formData);
// Success
toast.success('Form submitted!');
} catch (error) {
toast.error('Submission failed');
} finally {
setIsSubmitting(false);
}
};
return (
<form onSubmit={handleSubmit}>
<Input
value={formData.email}
onChange={(value) => setFormData({ ...formData, email: value })}
error={errors.email}
/>
<Button type="submit" loading={isSubmitting}>
Submit
</Button>
</form>
);
}function VideoList() {
const [videos, setVideos] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchVideos() {
try {
const response = await fetch('/api/videos');
const data = await response.json();
setVideos(data.videos);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchVideos();
}, []);
if (loading) return <Skeleton />;
if (error) return <ErrorMessage message={error} />;
return (
<VideoGrid videos={videos} />
);
}function MyPage() {
const [activeModal, setActiveModal] = useState<string | null>(null);
return (
<>
<Button onClick={() => setActiveModal('subscribe')}>
Subscribe
</Button>
<Modal
isOpen={activeModal === 'subscribe'}
onClose={() => setActiveModal(null)}
title="Subscribe to Newsletter"
>
<EmailCaptureForm
onSuccess={() => setActiveModal(null)}
/>
</Modal>
</>
);
}- Check import path
- Verify props are correct
- Check for TypeScript errors
- Inspect browser console
- Check Tailwind classes
- Verify CSS specificity
- Check responsive breakpoints
- Inspect with browser DevTools
- Use React DevTools Profiler
- Check for unnecessary re-renders
- Implement memoization
- Optimize images