Tech
[Blog] React Development That Satisfies the Single Responsibility Principle (SRP)
November 14, 2025
Have you ever heard of the SOLID principles?
Even if you’re new to software development, you’ve probably heard the name at least once. The SOLID principles are five software development guidelines in object-oriented design: SRP, OCP, LSP, ISP, and DIP.
SRP (Single Responsibility Principle)
OCP (Open Closed Principle)
LSP (Liskov Substitution Principle)
ISP (Interface Segregation Principle)
DIP (Dependency Inversion Principle)
Following these principles helps you create better object-oriented designs.
For frontend developers, especially those using React, a common question arises:
“How can I apply object-oriented principles in function-based React?”
At 60Hz, we use React across various domains including admin tools, dashboards, and user-facing web services.
React enables fast and declarative development but can make it easy to overlook design principles like SOLID. When deadlines loom, developers often pack API calls, state management, business logic, and UI rendering into a single component with the mindset “make it work first, refactor later.” It may feel fast initially, but ‘later’ rarely comes, and the code grows increasingly complex. Eventually, adding new features becomes intimidating, leading to adding more code instead of modifying existing code, which compounds the problem.
During my early years as a junior developer, I often felt trapped by design principles. Whenever I tried to apply a principle to a project, I focused solely on following it strictly rather than understanding why it mattered. This made adhering to principles uncomfortable, and eventually, I began to deliberately distance myself from them. Writing code felt cumbersome and time-consuming, and often the results were unsatisfactory.
Ironically, avoiding principles led me back to them. Why? Because development principles encapsulate philosophies for creating good software. They are distilled insights from developers who came before us, capturing both problems and solutions. Even if the concrete guidelines don’t fit every situation, the underlying philosophy can apply to any software development context. Taking a step back allowed me to apply the right philosophy at the right time.
This article starts from that awareness and explores how to apply the first SOLID principle, SRP (Single Responsibility Principle), in React code.
Single Responsibility Principle (SRP)
The core of the Single Responsibility Principle (SRP) is simple: a module (class, function, component) should have only one responsibility. Here, “responsibility” means the reason for change. Robert C. Martin phrased it as: “A module should be responsible to one, and only one, actor.”
Why is this important? When multiple responsibilities are combined in a single module, changing one aspect can unintentionally affect others. If responsibilities are clearly separated, the impact of changes is minimized, and the code becomes much easier to understand and test.
SRP in Real Life

Let’s look at a real-world example. Cars are made up of components, each with its own responsibility. If the AC breaks, you repair the AC; if the brakes fail, you replace the brakes. Each component can be serviced independently, making maintenance straightforward.

Now imagine the AC, brakes, and engine were merged into a single module. Replacing just the AC filter would require disassembling the entire module. You might accidentally touch the brake lines, causing brake fluid to leak, or miswire the engine, preventing it from starting. A simple AC fix could threaten critical functions like brakes or the engine. Naturally, repairs take longer and cost more.
Suppose a new AC component is developed. If the AC is separate, you can keep using the existing brakes and engine. But if the modules are combined, using the new AC would require creating a new module that combines the new AC, brakes, and engine, making the old module obsolete.
The same applies to software. Violating SRP leads to:
High cost even for small changes (increased maintenance costs)
Unexpected side effects when modifying code
Fear of breaking existing code leads to adding new code instead of modifying, making code more complex
Difficulty in reusing code (because multiple responsibilities are tangled)
Ultimately, SRP reduces maintenance costs and naturally improves reusability. In React, where components are frequently reused, adhering to SRP significantly enhances reusability.
Why SOLID Principles Are Easy to Miss in React Development
At 60Hz, we mainly use React for developing admin tools, dashboards, and user-facing web services. Because web development takes a large portion of our work, we actively use JavaScript and rely on React to enable declarative development that balances speed and maintainability. React is an excellent choice in many situations but also has characteristics that can lead developers to overlook SOLID principles.
JavaScript and React offer tremendous freedom. You can do anything inside a function, and a single component can contain both logic and UI. This freedom allows rapid feature implementation but also easily enables poor design. Languages like Java or C# enforce some design constraints through class structures and type systems, but in React, where everything is a "function," the boundaries of responsibility can become blurred.
When deadlines loom, developers often pack API calls, state management, business logic, and UI rendering into one component with the mindset, "make it work first, refactor later." It may feel fast initially, but that 'later' rarely comes, and the code becomes increasingly complex. Eventually, developers become hesitant to touch existing code, adding new code instead of modifying it, which compounds problems.
What ‘Responsibility’ Means in React Development
React development can ultimately be seen as function development. Business logic is implemented as functions, and even the UI is created using functions. What a function does is its responsibility. Therefore, in React, a responsibility includes not only “what business logic a function performs” but also “how and what UI it renders.”
Applying SRP in React
1. Separate Business Logic from UI
The first point to separate responsibilities is dividing business logic from the UI. In React, both business logic and UI are expressed as functions, so they are often combined.
For example, consider a MyProfile component. At first glance, it may seem like a single-responsibility component that simply displays your information. However, it tightly couples two responsibilities: “fetching my information” and “rendering the profile UI.”
What problems does this create?
The most immediate issue is reduced reusability. Suppose another screen needs the same UI but for a different user. Since MyProfile is tied to your information, it cannot be reused. You would have to create a new component.
Analyzing the component, we can see that the logic for fetching user information and the profile UI rendering logic are tightly coupled. Did you notice something odd? The same "profile UI rendering" responsibility exists in two components. And it’s not just two. Every time the same UI needs to be used elsewhere, a new component must be created, and we have no way of knowing how many more will be needed in the future.
This inevitably leads to maintenance issues. Even a simple change, such as "show '-' if the email is missing in the profile UI," requires manually updating many components, with a high chance of missing some.
Conversely, if business logic is reused in other components, the same code must be rewritten each time.
While coupling business logic and UI may seem natural at first glance, it is important to understand that it actually combines different responsibilities.
So, how should we fix this? As mentioned earlier, we can separate business logic from UI. Business logic can be placed in a Custom Hook, and UI can be handled by a separate component.
Now that we have separated responsibilities, let’s use the completed functions.
How does it look? The ProfileCard component now has a single responsibility: responding only to changes in the UI. By separating business logic from UI in this way, several advantages emerge.
Improved Reusability
ProfileCardcan now be used anywhere.Need to display a profile on a new page? You can simply reuse the
ProfileCard.If you need the business logic to fetch your information, you can reuse
UseMyInfo.
Easier Maintenance
UseMyInfo,UseUser: Responsibility for "how to fetch the data"ProfileCard: Responsibility for "how to display the profile"MyProfile,UserProfile: Responsibility for "how to combine which data with which UI"Each module has a clear single responsibility, so there is only one reason for any change. When performing maintenance, you can identify and modify only the relevant responsibility.
병렬 개발이 가능하다
API 서버 개발이 늦어지는 상황에서도 UI를 완성할 수 있고, 추후 비즈니스 로직 작성이 UI 컴포넌트를 수정하지 않습니다.
반대로 디자인이 늦어지는 상황에서도 비즈니스 로직을 먼저 작성할 수 있습니다.
Parallel development is possible.
Even if the API server development is delayed, the UI can be completed, and subsequent business logic development does not require modifications to the UI components.
Conversely, if the design is delayed, the business logic can be developed first.
Each responsibility can be easily tested.
2. UI Granularity
If business logic has been separated, the UI itself can also be divided into smaller responsibilities. Let’s take another look at the ProfileCard.
This component appears to have a single responsibility: rendering the profile card. In some cases, this might indeed be a single responsibility. However, upon closer inspection, multiple UI elements may be mixed depending on the situation.
Displaying avatar image
Displaying user name
Displaying email
Structuring the overall layout
What if the following requirements arise?
"Display user avatars and names in the comment section as well."
"Show only the avatar in a circular shape in the header."
"Make the email input field in the user edit form match the profile style."
With the current structure, reusing these small UI elements is difficult because multiple responsibilities are already mixed into the ProfileCard.
Just like business logic, UI components can also be split into smaller units of responsibility.
By breaking down the ProfileCard into individual responsibility units, each element can now be reused independently.
Separating UI logic into responsibility units improves reusability and maintainability. A common pattern for this is the Atomic Design Pattern.
It’s important to note that not all UI elements need to be broken down into smaller components. The unit of responsibility varies by company and by developer’s perception. What matters is identifying and managing responsibility units based on what is actually reused and can be changed independently. There’s no need to compulsively break down every element.
3. Granularity of Business Logic
Separating business logic from UI is not the end. Business logic itself can also be divided into multiple responsibilities. If a Custom Hook handles multiple tasks at once, it also violates the Single Responsibility Principle.
For example, consider developing a shopping cart feature.
This Hook has several responsibilities:
Fetching cart data
Calculating prices (total, discount, shipping)
Managing cart items
What problems might arise?
If the order page only needs price calculation, using useCart will also fetch unnecessary cart data.
The price calculation code is a pure function without external dependencies, yet testing it requires API mocking.
How can we separate responsibilities?
By dividing the logic, each module can be assembled where needed and reused independently. Testing also becomes much easier.
By separating business logic into appropriate responsibility units, we increased reusability and made maintenance easier. Like UI logic, there is no need to force business logic into excessively small units. Start with reasonable responsibility units and separate further only when necessary.
The key point is: when you feel a separation is needed, you must perform it.
Conclusion
In conclusion, we looked at how the Single Responsibility Principle can be applied in React. Beyond the benefits mentioned, there’s another advantage to separating responsibilities.
When delegating tasks to AI, it often produces much better results if responsibilities are separated into units, compared to giving AI a component with multiple mixed responsibilities. This approach is also easier to manage from the perspective of AI-generated outputs.
The Single Responsibility Principle is not exclusive to object-oriented programming. SOLID principles and other development principles are philosophies that can be applied in React development—or any type of development. What matters is not blindly following principles, but understanding their underlying philosophy and applying it appropriately to the situation.
Are your components carrying too many responsibilities? Are they struggling, and is it difficult for the maintainers too? Taking a moment to reflect will be valuable.
Ultimately, good design does not come from blindly following principles—it begins with understanding them and applying them selectively.

