UI Component Architecture
Skill Template: Frontend Development Category: Component Design & Composition Version: 1.0.0 Created: 2026-01-09
Quick Reference
When to use: UI Components, Props Design, Component Composition, Layout
Key Patterns:
Component Structure
- One component per file
- Props interface at top
- Hooks grouped together
- Event handlers as separate functions
- Return JSX at end
Composition over Inheritance
- Use children prop for flexibility
- Compound components for related UI
- Render props for logic sharing
- Container/Presentational separation
Props Design
- Explicit prop types (TypeScript interfaces)
- Sensible defaults for optional props
- Destructure in function signature
- Spread remaining props to root element
Performance Rules
- Memoize expensive calculations (useMemo)
- Memoize callbacks passed to children (useCallback)
- Use React.memo for pure presentational components
- Lazy load heavy components
Quick Example (React):
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
isLoading?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
export function Button({
variant = 'primary',
size = 'md',
isLoading = false,
children,
onClick,
...props
}: ButtonProps) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={isLoading}
{...props}
>
{isLoading ? <Spinner /> : children}
</button>
);
}
Anti-Patterns to Avoid:
- Prop drilling (use Context or state management)
- Giant components (split into smaller pieces)
- Inline styles (use CSS classes or styled-components)
- Business logic in components (extract to hooks/services)
Purpose
Design and implement scalable, reusable UI component architectures using modern composition patterns, component hierarchies, and design system principles.
When to Activate
Activate this skill when:
- Designing new UI component systems
- Building reusable component libraries
- Implementing design system components
- Refactoring component hierarchies
- Creating compound components
- Building component composition patterns
Core Capabilities
Component Design Patterns
- Atomic Design: Atoms, molecules, organisms, templates, pages
- Compound Components: Related components that work together
- Controlled vs Uncontrolled: Managing component state patterns
- Render Props: Component logic sharing
- Higher-Order Components: Component enhancement patterns
- Container/Presentational: Separating logic from presentation
Component Architecture
- Component composition over inheritance
- Single Responsibility Principle per component
- Props interface design and validation
- Component lifecycle optimization
- Performance considerations (memoization, lazy loading)
- Error boundaries and fault tolerance
Design System Integration
- Theme and design token consumption
- Consistent spacing and layout patterns
- Typography and color system usage
- Responsive design patterns
- Dark mode and theme switching
- Component variant systems
[TECH_STACK_SPECIFIC] Patterns
React Patterns
// Compound Component Pattern
const Card = ({ children, className }) => {
return <div className={`card ${className}`}>{children}</div>;
};
Card.Header = ({ children, className }) => {
return <div className={`card-header ${className}`}>{children}</div>;
};
Card.Body = ({ children, className }) => {
return <div className={`card-body ${className}`}>{children}</div>;
};
Card.Footer = ({ children, className }) => {
return <div className={`card-footer ${className}`}>{children}</div>;
};
// Usage
<Card>
<Card.Header>Title</Card.Header>
<Card.Body>Content</Card.Body>
<Card.Footer>Actions</Card.Footer>
</Card>
// Composition with Custom Hooks
const useToggle = (initialValue = false) => {
const [value, setValue] = useState(initialValue);
const toggle = useCallback(() => setValue(v => !v), []);
return [value, toggle];
};
const Accordion = ({ title, children }) => {
const [isOpen, toggle] = useToggle(false);
return (
<div className="accordion">
<button className="accordion-trigger">
{title}
</button>
{isOpen && <div className="accordion-content">{children}</div>}
</div>
);
};
// Render Props Pattern
const DataFetcher = ({ url, render }) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return render({ data, loading, error });
};
// Usage
<DataFetcher
url="/api/users"
render={({ data, loading, error }) => (
loading ? <Spinner /> : <UserList users={data} />
)}
/>
// Controlled Component Pattern
const Input = ({ value, onChange, label, error, ...props }) => {
return (
<div className="input-wrapper">
{label && <label className="input-label">{label}</label>}
<input
value={value}
=> onChange(e.target.value)}
className={`input ${error ? 'input-error' : ''}`}
{...props}
/>
{error && <span className="input-error-message">{error}</span>}
</div>
);
};
// Polymorphic Component Pattern
const Button = ({ as: Component = 'button', children, variant = 'primary', ...props }) => {
return (
<Component
className={`btn btn-${variant}`}
{...props}
>
{children}
</Component>
);
};
// Usage
<Button>Click me</Button>
<Button as="a" href="/link">Link Button</Button>
Vue Patterns
<!-- Compound Component Pattern with Slots -->
<template>
<div class="card">
<div v-if="$slots.header" class="card-header">
<slot name="header"></slot>
</div>
<div class="card-body">
<slot></slot>
</div>
<div v-if="$slots.footer" class="card-footer">
<slot name="footer"></slot>
</div>
</div>
</template>
<!-- Usage -->
<Card>
<template #header>Title</template>
<template #default>Content</template>
<template #footer>Actions</template>
</Card>
<!-- Composable Pattern (Vue 3) -->
<script setup>
import { ref, computed } from 'vue';
// useToggle composable
export function useToggle(initialValue = false) {
const value = ref(initialValue);
const toggle = () => { value.value = !value.value; };
return { value, toggle };
}
// Component using composable
const { value: isOpen, toggle } = useToggle(false);
</script>
<!-- Scoped Slots Pattern -->
<template>
<div class="data-fetcher">
<slot :data="data" :loading="loading" :error="error"></slot>
</div>
</template>
<script setup>
const props = defineProps(['url']);
const data = ref(null);
const loading = ref(true);
const error = ref(null);
// Fetch logic...
</script>
<!-- Usage -->
<DataFetcher url="/api/users" v-slot="{ data, loading }">
<Spinner v-if="loading" />
<UserList v-else :users="data" />
</DataFetcher>
Angular Patterns
// Component with Input/Output Pattern
@Component({
selector: 'app-card',
template: `
<div class="card">
<div class="card-header" *ngIf="header">
<ng-content select="[card-header]"></ng-content>
</div>
<div class="card-body">
<ng-content></ng-content>
</div>
<div class="card-footer" *ngIf="footer">
<ng-content select="[card-footer]"></ng-content>
</div>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CardComponent {
@Input() header: boolean = false;
@Input() footer: boolean = false;
}
// Container/Presentation Pattern
@Component({
selector: 'app-user-list-container',
template: `
<app-user-list-presentation
[users]="users$ | async"
[loading]="loading$ | async"
(userSelected)="onUserSelected($event)">
</app-user-list-presentation>
`
})
export class UserListContainerComponent {
users$ = this.userService.getUsers();
loading$ = this.userService.loading$;
constructor(private userService: UserService) {}
onUserSelected(user: User) {
// Handle user selection logic
}
}
// Smart/Dumb Component Pattern
@Component({
selector: 'app-user-list-presentation',
template: `
<div class="user-list">
<div *ngFor="let user of users"
(click)="userSelected.emit(user)"
class="user-item">
{{ user.name }}
</div>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserListPresentationComponent {
@Input() users: User[] = [];
@Input() loading: boolean = false;
@Output() userSelected = new EventEmitter<User>();
}
Tools Required
Development Tools
- Component development environment (Storybook, Ladle)
- Browser DevTools (React DevTools, Vue DevTools, Angular DevTools)
- Design token management tools
- Component documentation generators
Testing Tools
- Component testing framework (Testing Library, Vitest)
- Visual regression testing (Chromatic, Percy)
- Accessibility testing tools (axe, pa11y)
- Snapshot testing capabilities
Design Integration
- Design system documentation (Figma, Zeroheight)
- Design token export tools
- Component prop documentation generators
MCP Servers
[MCP_TOOLS]
Quality Checklist
Component Design
- Component has a single, clear responsibility
- Props interface is well-defined and typed
- Component is reusable across different contexts
- Follows project's component naming conventions
- Implements appropriate composition patterns
Performance
- Unnecessary re-renders are prevented (memo, useMemo, useCallback)
- Heavy computations are memoized
- Large lists use virtualization if needed
- Images are optimized and lazy-loaded
- Bundle size impact is acceptable
Accessibility
- Semantic HTML elements are used
- ARIA attributes are correctly applied
- Keyboard navigation works correctly
- Focus management is implemented
- Color contrast meets WCAG standards
- Screen reader compatible
Code Quality
- Component is properly typed (TypeScript/PropTypes)
- Props have sensible defaults where appropriate
- Error states are handled gracefully
- Loading states are communicated to users
- Component has unit tests
- Component is documented (JSDoc, Storybook)
Design System Compliance
- Uses design tokens for colors, spacing, typography
- Follows design system component patterns
- Implements all required variants
- Responsive behavior matches design specs
- Supports theming (light/dark mode)
Component Composition Examples
Building Complex UIs with Composition
// Base components (atoms)
const Button = ({ children, variant, ...props }) => (
<button className={`btn btn-${variant}`} {...props}>
{children}
</button>
);
const Icon = ({ name, size = 16 }) => (
<svg className="icon" width={size} height={size}>
{/* Icon content */}
</svg>
);
const Text = ({ as: Component = 'span', size, weight, children }) => (
<Component className={`text text-${size} font-${weight}`}>
{children}
</Component>
);
// Composite components (molecules)
const IconButton = ({ icon, label, ...props }) => (
<Button {...props}>
<Icon name={icon} />
{label && <Text>{label}</Text>}
</Button>
);
const Card = ({ title, children, actions }) => (
<div className="card">
<Card.Header>
<Text as="h3" size="lg" weight="semibold">{title}</Text>
</Card.Header>
<Card.Body>{children}</Card.Body>
{actions && <Card.Footer>{actions}</Card.Footer>}
</div>
);
// Complex components (organisms)
const UserProfile = ({ user }) => (
<Card
title={user.name}
actions={
<>
<IconButton icon="edit" label="Edit" variant="secondary" />
<IconButton icon="delete" label="Delete" variant="danger" />
</>
}
>
<div className="user-details">
<Text>{user.email}</Text>
<Text size="sm" weight="light">{user.bio}</Text>
</div>
</Card>
);
Advanced Patterns
Context-Based Component Communication
// Theme context for component styling
const ThemeContext = createContext();
export const ThemeProvider = ({ children, theme = 'light' }) => {
const [currentTheme, setTheme] = useState(theme);
return (
<ThemeContext.Provider value={{ theme: currentTheme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
export const useTheme = () => useContext(ThemeContext);
// Components consume theme
const ThemedButton = ({ children, ...props }) => {
const { theme } = useTheme();
return (
<button className={`btn btn-${theme}`} {...props}>
{children}
</button>
);
};
Component Slot Pattern
// Flexible layout component with named slots
const Layout = ({ header, sidebar, footer, children }) => (
<div className="layout">
{header && <div className="layout-header">{header}</div>}
<div className="layout-main">
{sidebar && <aside className="layout-sidebar">{sidebar}</aside>}
<main className="layout-content">{children}</main>
</div>
{footer && <div className="layout-footer">{footer}</div>}
</div>
);
// Usage with component composition
<Layout
header={<Header />}
sidebar={<Navigation />}
footer={<Footer />}
>
<PageContent />
</Layout>
Best Practices
Component Organization
- Keep components small and focused (< 200 lines)
- Extract complex logic to custom hooks/composables
- Group related components in feature folders
- Use index files for clean imports
Props Design
- Use descriptive prop names
- Provide prop types/interfaces
- Set sensible defaults
- Document complex props with comments
- Use object props for related values
Performance Optimization
- Memoize expensive computations
- Use React.memo/Vue computed for derived state
- Implement virtual scrolling for long lists
- Lazy load components when appropriate
- Optimize re-renders with proper dependencies
Testing Strategy
- Test component behavior, not implementation
- Test user interactions
- Test different prop combinations
- Test error states and edge cases
- Use accessibility testing tools
Remember: Good component architecture enables scalability, maintainability, and developer productivity. Focus on composition, reusability, and clear interfaces.