Claude
Skills
Sign in
Back

frontend-architecture

Included with Lifetime
$97 forever

Component architecture, design patterns, state management strategies, module systems, build tools, and scalable application structure

frontendarchitecturedesign-patternscomponentsscalabilitymodulesstate-management

What this skill does


# Frontend Architecture Skill

## When to Use This Skill

Use this skill when you need to:

- **Design scalable application architecture** - Structure large-scale frontend applications with maintainable patterns
- **Choose architectural patterns** - Select appropriate design patterns (MVC, MVVM, Flux) for your use case
- **Implement state management** - Design state architecture for complex applications
- **Structure component hierarchies** - Create reusable, composable component systems
- **Optimize build processes** - Configure bundlers and build tools for optimal performance
- **Plan testing strategies** - Architect comprehensive testing approaches across layers
- **Design module systems** - Implement code splitting, lazy loading, and module boundaries
- **Scale codebases** - Establish conventions for growing teams and applications
- **Refactor legacy code** - Migrate to modern architectural patterns
- **Performance optimization** - Structure applications for optimal load times and runtime performance

## Core Concepts

### 1. Component Architecture

Component-based architecture is the foundation of modern frontend development, enabling modularity and reusability.

#### Component Design Principles

**Single Responsibility Principle**
Each component should have one clear purpose:

```typescript
// Bad: Component doing too much
function UserDashboard() {
  const [user, setUser] = useState(null);
  const [posts, setPosts] = useState([]);
  const [notifications, setNotifications] = useState([]);
  const [settings, setSettings] = useState({});

  // Mixing concerns: data fetching, rendering, business logic
  useEffect(() => {
    fetch('/api/user').then(r => r.json()).then(setUser);
    fetch('/api/posts').then(r => r.json()).then(setPosts);
    fetch('/api/notifications').then(r => r.json()).then(setNotifications);
  }, []);

  return (
    <div>
      <header>{user?.name}</header>
      <PostList posts={posts} />
      <NotificationBell count={notifications.length} />
      <SettingsPanel settings={settings} />
    </div>
  );
}

// Good: Separated concerns
function UserDashboard() {
  return (
    <DashboardLayout>
      <UserHeader />
      <UserPosts />
      <UserNotifications />
      <UserSettings />
    </DashboardLayout>
  );
}

function UserPosts() {
  const { posts, loading } = useUserPosts();

  if (loading) return <PostsLoading />;
  return <PostList posts={posts} />;
}
```

**Composition Over Inheritance**

```typescript
// Using composition for flexibility
interface ButtonProps {
  children: React.ReactNode;
  onClick?: () => void;
  variant?: 'primary' | 'secondary';
}

function Button({ children, onClick, variant = 'primary' }: ButtonProps) {
  return (
    <button className={`btn btn-${variant}`} onClick={onClick}>
      {children}
    </button>
  );
}

// Compose complex components
function IconButton({ icon, ...props }: ButtonProps & { icon: string }) {
  return (
    <Button {...props}>
      <Icon name={icon} />
      {props.children}
    </Button>
  );
}

function LoadingButton({ loading, ...props }: ButtonProps & { loading: boolean }) {
  return (
    <Button {...props} disabled={loading}>
      {loading ? <Spinner /> : props.children}
    </Button>
  );
}
```

#### Container vs Presentational Components

```typescript
// Presentational Component (Pure UI)
interface UserCardProps {
  user: User;
  onEdit: () => void;
  onDelete: () => void;
}

function UserCard({ user, onEdit, onDelete }: UserCardProps) {
  return (
    <div className="user-card">
      <img src={user.avatar} alt={user.name} />
      <h3>{user.name}</h3>
      <p>{user.email}</p>
      <div className="actions">
        <button onClick={onEdit}>Edit</button>
        <button onClick={onDelete}>Delete</button>
      </div>
    </div>
  );
}

// Container Component (Logic & Data)
function UserCardContainer({ userId }: { userId: string }) {
  const { data: user, isLoading } = useQuery(['user', userId], () =>
    fetchUser(userId)
  );
  const deleteMutation = useMutation(deleteUser);
  const navigate = useNavigate();

  const handleEdit = () => navigate(`/users/${userId}/edit`);
  const handleDelete = () => {
    if (confirm('Delete user?')) {
      deleteMutation.mutate(userId);
    }
  };

  if (isLoading) return <Skeleton />;
  if (!user) return <ErrorState />;

  return (
    <UserCard
      user={user}
      onEdit={handleEdit}
      onDelete={handleDelete}
    />
  );
}
```

### 2. Separation of Concerns

#### Layer Architecture

```
┌─────────────────────────────────────┐
│        Presentation Layer           │
│     (Components, Views, UI)         │
├─────────────────────────────────────┤
│       Application Layer             │
│  (State Management, Routing, Hooks) │
├─────────────────────────────────────┤
│         Domain Layer                │
│   (Business Logic, Entities)        │
├─────────────────────────────────────┤
│      Infrastructure Layer           │
│    (API, Storage, Services)         │
└─────────────────────────────────────┘
```

**Example Implementation:**

```typescript
// Domain Layer - Business entities and logic
export class User {
  constructor(
    public id: string,
    public email: string,
    public name: string,
    public role: UserRole
  ) {}

  canEditPost(post: Post): boolean {
    return this.role === 'admin' || post.authorId === this.id;
  }

  get displayName(): string {
    return this.name || this.email.split('@')[0];
  }
}

// Infrastructure Layer - API communication
export class UserRepository {
  constructor(private apiClient: ApiClient) {}

  async findById(id: string): Promise<User> {
    const data = await this.apiClient.get(`/users/${id}`);
    return new User(data.id, data.email, data.name, data.role);
  }

  async save(user: User): Promise<void> {
    await this.apiClient.put(`/users/${user.id}`, {
      email: user.email,
      name: user.name,
      role: user.role
    });
  }
}

// Application Layer - State management
export function useUser(userId: string) {
  const repository = useUserRepository();

  return useQuery({
    queryKey: ['user', userId],
    queryFn: () => repository.findById(userId)
  });
}

// Presentation Layer - UI Component
export function UserProfile({ userId }: { userId: string }) {
  const { data: user, isLoading } = useUser(userId);

  if (isLoading) return <Loading />;

  return (
    <div>
      <h1>{user.displayName}</h1>
      <p>{user.email}</p>
    </div>
  );
}
```

## Design Patterns

### 1. Model-View-Controller (MVC)

```typescript
// Model - Data and business logic
class TodoModel {
  private todos: Todo[] = [];
  private observers: Set<(todos: Todo[]) => void> = new Set();

  addTodo(text: string) {
    const todo = { id: Date.now(), text, completed: false };
    this.todos.push(todo);
    this.notify();
  }

  toggleTodo(id: number) {
    const todo = this.todos.find(t => t.id === id);
    if (todo) {
      todo.completed = !todo.completed;
      this.notify();
    }
  }

  getTodos() {
    return [...this.todos];
  }

  subscribe(observer: (todos: Todo[]) => void) {
    this.observers.add(observer);
    return () => this.observers.delete(observer);
  }

  private notify() {
    this.observers.forEach(observer => observer(this.getTodos()));
  }
}

// Controller - Handles user input
class TodoController {
  constructor(private model: TodoModel) {}

  handleAddTodo(text: string) {
    if (text.trim()) {
      this.model.addTodo(text);
    }
  }

  handleToggleTodo(id: number) {
    this.model.toggleTodo(id);
  }
}

// View - React component
function TodoView() {
  const [model] = useState(() => new TodoModel());
  const [controller] = useState(() => new TodoController(model));
  const [todos, setTodos] = useState(model.getTodos());
  const [inputValue, setInputValue] = useState('');

  useEffect(() => {
    return model.subscribe(setTodos);
  }, [model]);

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    controller.handleAddTodo(inputValue);
  

Related in frontend