Component Architecture Guide
This guide covers the component structure and patterns used in the FeelyFeely Marketplace application.
Component Organization
Directory Structure
src/client/components/
├── account/ # User account related components
│ └── ChatButton.tsx # AI chat functionality
├── common/ # Shared/reusable components
├── forms/ # Form components
├── layout/ # Layout components (headers, footers, nav)
├── marketplace/ # Marketplace-specific components
├── maps/ # Map-related components
├── business/ # Business management components
├── post/ # Content posting components
│ └── WizardForm.tsx # Multi-step form wizard
└── ui/ # Basic UI components (buttons, inputs, etc.)
Component Patterns
1. Page Components
Page components are located in src/client/pages/ and represent full application views:
// Example page component structure
interface PageProps {
// Page-specific props
}
const ExamplePage: React.FC<PageProps> = () => {
// Page logic
return (
<div>
{/* Page content */}
</div>
);
};
export default ExamplePage;
Key Pages:
- MainPage - Landing page
- MarketplacePage - Product marketplace
- HomesPage - Real estate listings
- PlacesPage - Location-based services
- BusinessPage - Business management dashboard
- UserAccount - User profile management
- EntityListing - Create/edit listings
2. Layout Components
Layout components provide consistent structure across the application:
interface LayoutProps {
children: React.ReactNode;
title?: string;
}
const PageLayout: React.FC<LayoutProps> = ({ children, title }) => {
return (
<div className="min-h-screen bg-gray-50">
<Header title={title} />
<main className="container mx-auto px-4 py-8">
{children}
</main>
<Footer />
</div>
);
};
3. Form Components
Form components use React Hook Form for validation and state management:
interface FormProps {
onSubmit: (data: FormData) => void;
initialData?: Partial<FormData>;
}
const ExampleForm: React.FC<FormProps> = ({ onSubmit, initialData }) => {
const { register, handleSubmit, formState: { errors } } = useForm({
defaultValues: initialData
});
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* Form fields */}
</form>
);
};
4. Business Components
Components specific to business functionality:
WizardForm
Multi-step form for creating listings:
- Step navigation
- Form validation per step
- Progress indicator
- Data persistence between steps
BusinessAnalyzer
Market insights and analytics:
- Data visualization with Recharts
- Real-time statistics
- Export functionality
PromotionForm
Create business promotions:
- Date range selection
- Target audience settings
- Budget management
5. UI Components
Reusable UI components following design system patterns:
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'outline';
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
}
const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
children,
...props
}) => {
const baseClasses = 'font-medium rounded-lg transition-colors';
const variantClasses = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-600 text-white hover:bg-gray-700',
outline: 'border border-gray-300 text-gray-700 hover:bg-gray-50'
};
const sizeClasses = {
sm: 'px-3 py-2 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg'
};
return (
<button
className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`}
{...props}
>
{children}
</button>
);
};
State Management in Components
Zustand Store Integration
Components connect to Zustand stores for global state:
import { useUserStore } from '@stores/user';
import { useClientStore } from '@stores/client';
const UserProfile: React.FC = () => {
const user = useUserStore(state => state.user);
const updateUser = useUserStore(state => state.updateUser);
// Component logic
};
React Query for Server State
Components use React Query for server data:
import { useQuery, useMutation } from '@tanstack/react-query';
const ProductList: React.FC = () => {
const { data: products, isLoading } = useQuery({
queryKey: ['products'],
queryFn: fetchProducts
});
const createProductMutation = useMutation({
mutationFn: createProduct,
onSuccess: () => {
// Handle success
}
});
// Component render logic
};
Component Communication
Props Pattern
Parent to child communication via props:
interface ChildProps {
data: DataType;
onAction: (value: string) => void;
}
const Parent: React.FC = () => {
const handleAction = (value: string) => {
// Handle action
};
return (
<Child
data={someData}
onAction={handleAction}
/>
);
};
Context Pattern
For deeply nested prop drilling:
const ThemeContext = createContext<ThemeContextType>();
const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
Event Emitters
For cross-component communication:
// Custom event bus
export const eventBus = new EventTarget();
// Component A - emitting
const emitEvent = () => {
eventBus.dispatchEvent(new CustomEvent('data-updated', {
detail: { data: 'new data' }
}));
};
// Component B - listening
useEffect(() => {
const handleDataUpdate = (event: CustomEvent) => {
// Handle event
};
eventBus.addEventListener('data-updated', handleDataUpdate);
return () => eventBus.removeEventListener('data-updated', handleDataUpdate);
}, []);
Animation Patterns
Framer Motion Integration
Components use Framer Motion for animations:
import { motion } from 'framer-motion';
const AnimatedCard: React.FC = ({ children }) => {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
className="card"
>
{children}
</motion.div>
);
};
Custom Animation Classes
CSS classes for consistent animations:
/* App.css custom animations */
.animate-bounce-out {
animation: bounce-out 1s ease-out forwards;
}
.animate-collection-select {
animation: collection-select 0.5s ease-out;
}
.animate-heart-pulse {
animation: heart-pulse 0.6s ease-in-out;
}
Error Handling
Error Boundaries
Components use error boundaries for graceful failure:
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return (
<div className="error-fallback">
<h3>Something went wrong</h3>
<p>Please try refreshing the page</p>
</div>
);
}
return this.props.children;
}
}
Loading States
Components handle loading states consistently:
const DataComponent: React.FC = () => {
const { data, isLoading, error } = useQuery(['data'], fetchData);
if (isLoading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
if (!data) return <EmptyState />;
return <DataDisplay data={data} />;
};
Best Practices
1. Component Composition
- Prefer composition over inheritance
- Use render props or children for flexibility
- Create small, focused components
2. Performance Optimization
- Use React.memo for expensive renders
- Implement useCallback and useMemo appropriately
- Lazy load components with React.lazy
3. Accessibility
- Include proper ARIA labels
- Ensure keyboard navigation
- Maintain semantic HTML structure
4. Testing
- Write tests for component behavior
- Test user interactions
- Mock external dependencies
5. TypeScript Integration
- Define strict prop interfaces
- Use discriminated unions for variants
- Implement proper generic constraints
Styling Guidelines
Tailwind CSS Patterns
// Consistent class composition
const buttonBaseClasses = 'inline-flex items-center justify-center rounded-md border border-transparent px-4 py-2 text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2';
const buttonVariants = {
primary: 'bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-500',
secondary: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 focus:ring-indigo-500',
};
Responsive Design
// Mobile-first responsive classes
const ResponsiveGrid: React.FC = ({ children }) => {
return (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{children}
</div>
);
};
This component architecture ensures maintainable, scalable, and performant React components throughout the FeelyFeely Marketplace application.