How to Use System Design in Your React Project

When you start a new React project, the initial momentum is great. Components are flying, features are getting built, and everything feels clean. But as the codebase grows, that initial structure often crumbles. Files get dumped everywhere, components become bloated, and simple changes start to have ripple effects. I’ve been there, and it’s not a fun place to be.

This is where system design for the frontend becomes essential. It’s not just about backend architecture or a massive corporate process; it’s a practical framework for making consistent, scalable decisions about your project’s structure from day one. I believe adopting this mindset is a critical skill for any mid-level developer looking to build maintainable, long-lasting applications.

What System Design Looks Like for a React Developer

For us, system design in React is really about creating a design system. This isn’t just a fancy term for a component library; it’s a unified collection of principles, components, and styles that guide the entire user experience.

My approach often follows the principles of Atomic Design, which breaks down the UI into a hierarchy of building blocks:

  • Atoms: The smallest, most fundamental elements. Think Button, Input, Label. They have no internal state and are completely independent.
  • Molecules: Groups of atoms working together. For example, a search bar molecule might combine an Input, a Button, and a Label.
  • Organisms: Complex UI components made up of molecules and atoms. A navigation bar is a perfect example, combining a logo, navigation links (molecules), and perhaps a user avatar.

This mental model helps you think about reusability from the ground up, ensuring you’re not rebuilding the same button variant a dozen times across your app.

But why do I need to create such small components? Because I want all the Button to have the same color, hover effect, font and behavior. Same of the Input and the associated Label.

Structuring Your Project to Avoid Chaos

How you organize your files is arguably the most impactful system design decision you’ll make. I’ve seen projects where components are scattered randomly and others where everything is neatly tucked into a single components folder. Both approaches fall apart at scale.

For any project of a decent size, I strongly advocate for a feature-based folder structure. Instead of grouping files by type, you group them by their business domain or feature.

Here’s an example of how I typically structure a project:

src/
├── components/          # Reusable, atomic-level UI components
│   ├── Button/
│   ├── Card/
│   └── ...
├── features/            # Feature-specific, domain-driven code
│   ├── UserProfile/
│   │   ├── UserProfilePage.js       # The main page component
│   │   ├── components/            # Feature-specific components
│   │   │   └── UserCard.js
│   │   ├── services/              # API calls & business logic
│   │   │   └── userService.js
│   │   ├── UserProfile.css
│   │   └── ...
├── hooks/               # Custom hooks for reusable logic
├── utils/               # General utility functions
└── App.js

This structure ensures a feature’s code is co-located, making it easier to understand, maintain, and even delete without affecting the rest of the application.

Build Your Foundational Components First

Before you write the first line of application logic, take a moment to build your core design components. This is the foundation of your system. These should be presentational and highly composable.

Let’s start with a simple but crucial component: Button.

import React from 'react';
import './Button.css';

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

export default Button;

And the associated style.

.btn {
  padding: 1rem 2rem;
  border: none;
  cursor: pointer;
}
.btn-primary {
  background-color: #007bff;
  color: #fff;
}
/* Add other variants like danger, secondary, etc. */

By creating this single source of truth for your button, you guarantee a consistent user interface across your entire application. It also makes it trivial to update the button’s style globally later on.

From a Component Library to a Fully Fledged Feature

Now that you have your core components, you can start building. Your features will act as the “organisms” and “templates” from our earlier discussion.

In our UserProfile feature, the UserProfilePage.js file handles the data fetching and logic, while a separate, reusable component like UserCard.js (from the feature’s sub-folder) handles just the visual rendering. This is the container/presentational pattern in action.

import React, { useState, useEffect } from 'react';
import UserCard from './components/UserCard';
import { getUserData } from './services/userService';
import Button from '../../components/Button/Button';

const UserProfilePage = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const fetchUser = async () => {
      const data = await getUserData();
      setUser(data);
    };
    fetchUser();
  }, []);

  if (!user) {
    return <div>Loading user data...</div>;
  }

  return (
    <div>
      <h1>Your Profile</h1>
      <UserCard user={user} />
      <Button onClick={() => console.log('This is a core component')}>
        Edit Profile
      </Button>
    </div>
  );
};

export default UserProfilePage;

Notice how UserProfilePage is the “smart” component that knows how to get data, while UserCard is the “dumb” component that simply receives props and renders the UI. This clear separation of concerns is a hallmark of good system design.

The result is a project that is scalable, easy to navigate, and a pleasure to work with, even years after its initial launch.


Never Miss Another Tech Innovation

Concrete insights and actionable resources delivered straight to your inbox to boost your developer career.

My New ebook, Best Practices To Create A Backend With Spring Boot 3, is available now.

Best practices to create a backend with Spring Boot 3

Leave a comment

Discover more from The Dev World - Sergio Lema

Subscribe now to keep reading and get access to the full archive.

Continue reading