Component Development Guide
Summary Overview
Legacy walkthrough covering the component patterns, hooks, and animation recipes that powered the earlier version of IvannDocs.
Updated: 2025-11-24
reactcomponentspatterns
Component Development Guide
Modern React Component Patterns
Component Structure Best Practices
Basic Component Template
import React from 'react';
interface ComponentProps {
title: string;
description?: string;
className?: string;
}
const MyComponent: React.FC<ComponentProps> = ({
title,
description,
className
}) => {
return (
<div className={`component-base ${className}`}>
<h2>{title}</h2>
{description && <p>{description}</p>}
</div>
);
};
export default MyComponent;
Component with Hooks
import React, { useState, useEffect } from 'react';
const InteractiveComponent: React.FC = () => {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
// Component logic here
}, []);
return (
<div className="interactive-component">
{/ Component content /}
</div>
);
};
UI Component Patterns
Button Component Pattern
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'outline';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
onClick?: () => void;
children: React.ReactNode;
}
const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
disabled = false,
onClick,
children
}) => {
const baseClasses = 'btn-base transition-all duration-300';
const variantClasses = {
primary: 'bg-primary text-white hover:bg-primary/90',
secondary: 'bg-secondary text-white hover:bg-secondary/90',
outline: 'border border-primary text-primary hover:bg-primary hover:text-white'
};
const sizeClasses = {
sm: 'px-3 py-1 text-sm',
md: 'px-4 py-2',
lg: 'px-6 py-3 text-lg'
};
return (
<button
className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
};
Card Component Pattern
interface CardProps {
children: React.ReactNode;
className?: string;
variant?: 'default' | 'glass' | 'bordered';
}
const Card: React.FC<CardProps> = ({
children,
className = '',
variant = 'default'
}) => {
const variants = {
default: 'bg-white shadow-md',
glass: 'bg-white/10 backdrop-blur-lg border border-white/20',
bordered: 'bg-white border-2 border-gray-200'
};
return (
<div className={`rounded-lg p-6 ${variants[variant]} ${className}`}>
{children}
</div>
);
};
Animation Patterns
GSAP Animation Hook
import { useEffect, useRef } from 'react';
import { gsap } from 'gsap';
const useGSAPAnimation = () => {
const elementRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const ctx = gsap.context(() => {
gsap.from(elementRef.current, {
opacity: 0,
y: 50,
duration: 1,
ease: "power2.out"
});
}, elementRef);
return () => ctx.revert();
}, []);
return elementRef;
};
Scroll-Triggered Animations
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
const AnimatedSection: React.FC = () => {
const sectionRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const ctx = gsap.context(() => {
gsap.from('.animate-item', {
opacity: 0,
y: 100,
duration: 1,
stagger: 0.2,
scrollTrigger: {
trigger: sectionRef.current,
start: 'top 80%',
toggleActions: 'play none none reverse'
}
});
}, sectionRef);
return () => ctx.revert();
}, []);
return (
<section ref={sectionRef}>
<div className="animate-item">Item 1</div>
<div className="animate-item">Item 2</div>
<div className="animate-item">Item 3</div>
</section>
);
};
Responsive Design Patterns
Mobile-First Approach
/ Base styles for mobile /
.component {
padding: 1rem;
font-size: 0.875rem;
}
/ Tablet and up /
@media (min-width: 768px) {
.component {
padding: 1.5rem;
font-size: 1rem;
}
}
/ Desktop and up /
@media (min-width: 1024px) {
.component {
padding: 2rem;
font-size: 1.125rem;
}
}
Responsive Hook
import { useState, useEffect } from 'react';
const useResponsive = () => {
const [screenSize, setScreenSize] = useState({
isMobile: false,
isTablet: false,
isDesktop: false
});
useEffect(() => {
const checkScreenSize = () => {
const width = window.innerWidth;
setScreenSize({
isMobile: width < 768,
isTablet: width >= 768 && width < 1024,
isDesktop: width >= 1024
});
};
checkScreenSize();
window.addEventListener('resize', checkScreenSize);
return () => window.removeEventListener('resize', checkScreenSize);
}, []);
return screenSize;
};
Custom Hooks Patterns
Form Handling Hook
const useForm = <T>(initialValues: T) => {
const [values, setValues] = useState<T>(initialValues);
const [errors, setErrors] = useState<Partial<T>>({});
const handleChange = (name: keyof T, value: any) => {
setValues(prev => ({ ...prev, [name]: value }));
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: undefined }));
}
};
const validate = (validationRules: any) => {
// Validation logic
};
return { values, errors, handleChange, validate };
};
API Data Hook
const useApiData = <T>(url: string) => {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
Testing Patterns
Component Testing
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
describe('Button Component', () => {
test('renders with correct text', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
test('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
test('applies correct variant styles', () => {
render(<Button variant="primary">Primary Button</Button>);
const button = screen.getByText('Primary Button');
expect(button).toHaveClass('bg-primary');
});
});
Hook Testing
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';
test('should increment counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
Performance Optimization
React.memo Usage
const ExpensiveComponent = React.memo<Props>(({ data, onUpdate }) => {
// Expensive rendering logic
return <div>{/ component content /}</div>;
}, (prevProps, nextProps) => {
// Custom comparison function
return prevProps.data.id === nextProps.data.id;
});
useMemo and useCallback
const OptimizedComponent: React.FC<Props> = ({ items, filter }) => {
const filteredItems = useMemo(() => {
return items.filter(item => item.category === filter);
}, [items, filter]);
const handleItemClick = useCallback((id: string) => {
// Handle click logic
}, []);
return (
<div>
{filteredItems.map(item => (
<Item
key={item.id}
data={item}
onClick={handleItemClick}
/>
))}
</div>
);
};
Error Handling
Error Boundary
class ErrorBoundary extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <div>Something went wrong.</div>;
}
return this.props.children;
}
}
Error Hook
const useErrorHandler = () => {
const [error, setError] = useState<Error | null>(null);
const handleError = useCallback((error: Error) => {
setError(error);
// Log to error reporting service
}, []);
const clearError = useCallback(() => {
setError(null);
}, []);
return { error, handleError, clearError };
};
This guide provides patterns and best practices for building modern, scalable React components with TypeScript, animations, and responsive design.