Polygon Technology

15 Days of React Design Patterns | Frontend Architecture Guide

15 Days of React Design Patterns | Frontend Architecture Guide

Learn React Design Patterns in 15 days with examples. Build scalable frontend architecture using React & JavaScript best practices.
Frontend
January 25, 2026
Avatar
Al muntasir

In React, a design pattern is a reusable way to structure components, state, and logic so your app is easier to understand, scale, and maintain. React doesn’t enforce a single pattern—developers choose patterns based on the problem they’re solving.

Below are the most common React design patterns, with clear explanations and examples.

Day 1: Container–Presenter Pattern

What it is :

The Container–Presenter pattern separates the behavior of data from its presentation.

  • Container Components manage:
    • State
    • Data fetching
    • Side effects
    • Business rules
  • Presenter Components manage:
    • JSX markup
    • Styling
    • UI layout
    • User interaction callbacks

Why it exists

As applications grow, mixing logic and UI makes components hard to:

  • Test
  • Reuse
  • Maintain

This pattern enforces the Single Responsibility Principle.

Diagram

┌──────────────┐
│  Container   │
│──────────────│
│ State        │
│ API Calls    │
│ Logic        │
└──────┬───────┘
       │ props
┌──────▼───────┐
│ Presenter    │
│──────────────│
│ JSX / UI     │
│ Styling      │
└──────────────┘

Detailed Example

// UserContainer.jsx
function UserContainer() {
  const [user, setUser] = React.useState(null);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    fetch('/api/user')
      .then(res => res.json())
      .then(data => {
        setUser(data);
        setLoading(false);
      });
  }, []);

  return (
    <UserPresenter
      user={user}
      loading={loading}
    />
  );
}

// UserPresenter.jsx
function UserPresenter({ user, loading }) {
  if (loading) return <p>Loading...</p>;
  return <h1>Welcome, {user.name}</h1>;
}

When to use

  • Design systems
  • Enterprise applications
  • Shared UI libraries

Day 2: Controlled vs Uncontrolled Components Pattern

What it is

Defines who owns the form state — React or the DOM.

Controlled Components

React fully controls the input value.

Uncontrolled Components

The DOM keeps the state, accessed via refs.

Diagram

Controlled:
User Input → React State → UI

Uncontrolled:
User Input → DOM State

Detailed Example

// Controlled Input
function ControlledForm() {
  const [email, setEmail] = React.useState('');

  return (
    <input
      value={email}
      onChange={e => setEmail(e.target.value)}
    />
  );
}

// Uncontrolled Input
function UncontrolledForm() {
  const inputRef = React.useRef();

  const submit = () => alert(inputRef.current.value);

  return (
    <>
      <input ref={inputRef} />
      <button onClick={submit}>Submit</button>
    </>
  );
}

When to use

  • Controlled: Validation, dynamic UI
  • Uncontrolled: Simple forms, better performance

Day 3: Compound Components Pattern

What it is

Allows components to share state without explicit props implicitly.

Why it exists

  • Flexible APIs
  • Declarative structure
  • Avoid prop drilling

Diagram

<Tabs>
 ├── <Tabs.List>
 ├── <Tabs.Tab />
 └── <Tabs.Panel />

Detailed Example

const TabsContext = React.createContext();

function Tabs({ children }) {
  const [active, setActive] = React.useState(0);

  return (
    <TabsContext.Provider value={{ active, setActive }}>
      {children}
    </TabsContext.Provider>
  );
}

function Tab({ index, children }) {
  const { setActive } = React.useContext(TabsContext);
  return <button onClick={() => setActive(index)}>{children}</button>;
}

function Panel({ index, children }) {
  const { active } = React.useContext(TabsContext);
  return active === index ? <div>{children}</div> : null;
}

Day 4: Render Props Pattern

What it is

A component receives a function prop to decide what to render.

A component receives a function prop to decide what to render.

Diagram

Component
 └── render(data) => JSX

Detailed Example

function MouseTracker({ render }) {
  const [pos, setPos] = React.useState({ x: 0, y: 0 });

  return (
    <div onMouseMove={e => setPos({ x: e.clientX, y: e.clientY })}>
      {render(pos)}
    </div>
  );
}

<MouseTracker
  render={({ x, y }) => <p>{x}, {y}</p>}
/>

Day 5: Higher‑Order Components (HOC)

What it is

A function that takes a component and returns an enhanced component.

Diagram

Component → HOC → Enhanced Component

Detailed Example

function withLogger(Component) {
  return function Wrapped(props) {
    console.log('Props:', props);
    return <Component {...props} />;
  };
}

Day 6: Custom Hooks Pattern

What it is

Extract reusable logic into composable hooks.

Diagram

Component
 └── useCustomHook()

Detailed Example

function useToggle(initial = false) {
  const [value, setValue] = React.useState(initial);
  const toggle = () => setValue(v => !v);
  return [value, toggle];
}

Day 6: Custom Hooks Pattern

What it is

Extract reusable logic into composable hooks.

Diagram

Component
 └── useCustomHook()

Detailed Example

function useToggle(initial = false) {
  const [value, setValue] = React.useState(initial);
  const toggle = () => setValue(v => !v);
  return [value, toggle];
}

Day 7: Provider Pattern (Context API)

What it is

Share global state without prop drilling.

Diagram

<Provider>
 └── <App>
     └── <Child />

Detailed Example

const AuthContext = React.createContext();

function AuthProvider({ children }) {
  const [user, setUser] = React.useState(null);
  return (
    <AuthContext.Provider value={{ user, setUser }}>
      {children}
    </AuthContext.Provider>
  );
}

Day 8: State Reducer Pattern

What it is

Allows consumers to override internal state logic.

Diagram

Action → Reducer → State

Detailed Example

function reducer(state, action) {
  switch (action.type) {
    case 'increment': return state + 1;
    default: return state;
  }
}

Day 9: Observer / Pub‑Sub Pattern

What it is

Decoupled event‑based communication.

Diagram

Publisher → Event → Subscribers

Detailed Example

const events = {};
export const subscribe = (e, fn) => (events[e] ||= []).push(fn);
export const publish = (e, data) => events[e]?.forEach(fn => fn(data));

Day 10: Performance Patterns

What it is

Reduce unnecessary renders.

Techniques

  • React.memo
  • useMemo
  • useCallback

Example

const MemoButton = React.memo(Button);

Day 11: Slot Pattern

What it is

Explicit UI placeholders.

Diagram

<Card>
 ├── Header
 ├── Body
 └── Footer

Detailed Example

function Card({ header, footer, children }) {
  return (
    <div>
      {header}
      {children}
      {footer}
    </div>
  );
}

Day 12: Hooks Factory / Strategy Pattern

What it is

Select behavior dynamically.

Example

function usePayment(type) {
  return type === 'paypal' ? usePaypal() : useStripe();
}

Day 13: Facade Pattern

What it is

Hide complexity behind a simple interface.

Example

export const api = {
  login: () => fetch('/login'),
  logout: () => fetch('/logout')
};

Day 14: Error Boundary Pattern
What it is

Catch rendering errors safely.

Example

class ErrorBoundary extends React.Component {
  state = { error: false };
  static getDerivedStateFromError() { return { error: true }; }
  render() { return this.state.error ? <h1>Error</h1> : this.props.children; }
}

Day 15: Suspense Pattern

What it is

Declarative async loading.

Example

<Suspense fallback={<Loader />}>
  <Profile />
</Suspense>

Conclusion

React Design Patterns provide reusable solutions for structuring components, state, and logic. Use them when your application grows in complexity, and apply only the patterns that solve the problem you are facing.