Documentation
Components
Component Types

Component Types

Understanding the different types of components and when to use each.

Presentational Components

Pure components that receive data via props and focus on UI rendering.

interface UserAvatarProps {
  src: string;
  alt: string;
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}
 
export const UserAvatar: React.FC<UserAvatarProps> = ({
  src,
  alt,
  size = 'md',
  className,
}) => {
  const sizeClasses = {
    sm: 'w-8 h-8',
    md: 'w-12 h-12',
    lg: 'w-16 h-16',
  };
 
  return (
    <img
      src={src}
      alt={alt}
      className={cn('rounded-full', sizeClasses[size], className)}
    />
  );
};

Characteristics:

  • No internal state (or minimal UI state)
  • No side effects
  • Easily testable
  • Highly reusable

Container Components

Handle data fetching, state management, and business logic.

export const UserProfileContainer: React.FC = () => {
  const { userId } = useParams<{ userId: string }>();
  const { data: user, isLoading, error } = useUserQuery(userId);
 
  if (isLoading) return <ProfileSkeleton />;
  if (error) return <ErrorMessage error={error} />;
  if (!user) return <NotFound />;
 
  return <UserProfile user={user} />;
};

Characteristics:

  • Fetch and manage data
  • Handle side effects
  • Pass data to presentational components
  • Usually map to routes

Compound Components

Components that work together to form a cohesive UI pattern.

// Usage
<Card>
  <Card.Header>
    <Card.Title>User Profile</Card.Title>
    <Card.Actions>
      <Button>Edit</Button>
    </Card.Actions>
  </Card.Header>
  <Card.Body>
    <p>Content goes here</p>
  </Card.Body>
  <Card.Footer>
    <Button variant="secondary">Cancel</Button>
    <Button>Save</Button>
  </Card.Footer>
</Card>

Implementation:

const CardContext = createContext<{ variant?: string }>({});
 
const Card = ({ children, variant }: CardProps) => (
  <CardContext.Provider value={{ variant }}>
    <div className="rounded-lg border">{children}</div>
  </CardContext.Provider>
);
 
Card.Header = ({ children }: { children: ReactNode }) => (
  <div className="border-b p-4">{children}</div>
);
 
Card.Title = ({ children }: { children: ReactNode }) => (
  <h3 className="text-lg font-semibold">{children}</h3>
);
 
Card.Body = ({ children }: { children: ReactNode }) => (
  <div className="p-4">{children}</div>
);
 
Card.Footer = ({ children }: { children: ReactNode }) => (
  <div className="border-t p-4 flex gap-2">{children}</div>
);

Layout Components

Handle page structure and layout concerns.

interface PageLayoutProps {
  title: string;
  actions?: ReactNode;
  children: ReactNode;
}
 
export const PageLayout: React.FC<PageLayoutProps> = ({
  title,
  actions,
  children,
}) => (
  <div className="min-h-screen">
    <header className="border-b">
      <div className="flex items-center justify-between p-4">
        <h1 className="text-2xl font-bold">{title}</h1>
        {actions && <div className="flex gap-2">{actions}</div>}
      </div>
    </header>
    <main className="p-6">{children}</main>
  </div>
);

Higher-Order Components (HOCs)

Wrap components to add functionality. Use sparingly — prefer hooks.

function withAuthentication<P extends object>(
  WrappedComponent: React.ComponentType<P>
) {
  return function AuthenticatedComponent(props: P) {
    const { isAuthenticated, isLoading } = useAuth();
 
    if (isLoading) return <LoadingSpinner />;
    if (!isAuthenticated) return <Navigate to="/login" />;
 
    return <WrappedComponent {...props} />;
  };
}
 
// Usage
const ProtectedDashboard = withAuthentication(Dashboard);