Skip to main content

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.