Skip to main content

Command Palette

Search for a command to run...

1.9 Core React Architecture (Component, Container, Smart/Dumb, Data Flow)

Updated
5 min readView as Markdown

While many frontend architecture concepts are generic enough to work across frameworks like Angular or Vue, React has a highly specialized core architecture. Designing enterprise applications requires a deep understanding of component relationships, predictable data flow, and separation of concerns.

This guide outlines the architectural patterns that keep React applications clean, maintainable, and scalable.


1. Component-Based Architecture

In React, everything is a component. Your header, your footer, and even entire pages are structured as components managed by a router.

Best Practices for Designing Components:

  • Upfront Analysis: Analyze your visual mockups before writing code. Identify elements that repeat (e.g., custom buttons, search bars, rounded avatar frames).

  • Avoid Inline Bloat: Do not mix raw HTML structures with unique feature components in a chaotic layout. If a piece of UI is repeated or grows past a single responsibility, extract it immediately.

  • Keep Code Maintainable: Fragmenting the UI properly prevents tangled code and eliminates hard-to-track bugs as the feature expands.


2. Container vs. Presentational Patterns

To build a modular codebase, draw a clear line between logic execution and UI rendering.

Presentational Components (The "How it Looks" Layer)

  • Often referred to as Dumb Components.

  • Their sole job is to accept data via props and render it visually to the UI.

  • They hold zero business logic, do not fetch APIs, and rarely manage complex state. This makes them highly reusable and composable.

Container Components (The "How it Works" Layer)

  • Often referred to as Smart Components.

  • They handle API integration, data manipulation, state management, and business logic calculations.

  • They wrap presentational components and feed them processed data.

// 1. Presentational Component (Dumb) - Highly Reusable
function UserProfileCard({ name, email }) {
  return (
    <div className="card-container">
      <h3>{name}</h3>
      <p>{email}</p>
    </div>
  );
}

// 2. Container Component (Smart) - Manages Orchestration
import { useState, useEffect } from 'react';

export default function UserProfileContainer() {
  const [userData, setUserData] = useState(null);

  useEffect(() => {
    // API logic lives securely in the container layer
    fetch('/api/user/profile')
      .then((res) => res.json())
      .then((data) => setUserData(data));
  }, []);

  return <UserProfileCard name={userData?.name} email={userData?.email} />;
}

3. Smart vs. Dumb Components in Global State (Redux)

While Container and Presentational components typically follow a direct Parent-Child layout structure, the Smart and Dumb paradigm can also be applied across parallel sibling components, especially when state management libraries like Redux or Zustand are introduced.

       [ Redux Global Store ]
         /                \
  (Selects Data)     (Dispatches Actions)
       /                    \
[ Smart Component ]     [ Dumb Component ]
(Data Processor)        (Visual Button)
  • Parallel Operations: A component can become a "Smart Component" simply by connecting directly to a global state manager or custom hook without needing to act as a parent container.

  • Decoupled Architecture: This decoupling means you can separate calculations from visual rendering across entirely separate branches of your application tree.


4. Architectural Data Flow & State Placement

Choosing where your data lives and how it moves across screens is a critical system design decision.

Controlled vs. Uncontrolled Components

This concept primarily governs how forms and user inputs handle their data:

  • Controlled: The input's value is driven entirely by React state (value={state}). Every keystroke triggers a state change. Choose this for instant validation or dynamic form behaviors.

  • Uncontrolled: The DOM itself handles the data storage internally. React accesses values on demand using a ref (e.g., inputRef.current.value). Choose this for basic forms to avoid unnecessary re-renders.

Prop Drilling vs. Lifting State Up

When sharing information between views, choose the right communication strategy:

  • Prop Drilling: Passing parameters down through several intermediate child components to reach a deep nested layer. Use this sparingly for short depths, as deep drilling creates rigid dependencies.

  • Lifting State Up: Moving state to the closest common parent component so that two separate child components can read and share the same fluctuating data.


Interview Q&A

Q1: What is the difference between a Container component and a Presentational component?

Answer: A Presentational component is focused purely on the UI. It receives data through props and displays it, making it highly reusable. A Container component focuses on the logic. It handles things like fetching data from an API, sorting information, or managing state, and then hands that prepared data to the presentational component to show on the screen.

Q2: What are "Smart" and "Dumb" components, and do they always have to be Parent and Child?

Answer: "Dumb" components just render whatever UI data you feed them, while "Smart" components contain the logic and processing code. They do not always have to be parent and child. If you are using a global state tool like Redux, you can have parallel or sibling components where one acts as a smart component by managing global data, while another remains a dumb component by just reading a value through a hook.

Q3: When should I choose a Controlled component over an Uncontrolled component for forms?

Answer: You should use a Controlled component when you need real-time control over an input field—like validating a password while the user types, or disabling a submit button until a form is completely filled. You can use an Uncontrolled component when you just need to collect form data once upon submission and want to avoid causing performance-slowing re-renders on every single keystroke.

Q4: What does "Lifting State Up" mean in React system design?

Answer: In React, data flows downward from parent to child. If two separate child components need to share the same changing piece of data, they can't talk to each other directly. To fix this, we "lift" that state up by placing it into their closest shared parent component. The parent then passes that state down to both children as props, keeping them perfectly in sync.