Smart vs Dumb Components in Angular: A Practical Guide to Better Reusability


When I started working with Angular, I thought I was doing it right — following tutorials, implementing components based on functionality, and quickly building out screens. But as my projects grew, I began facing some major problems.
In the middle of tight deadlines and complex functionality, I realized that my component architecture was messy. I was creating components for each individual functionality without considering reusability. This was fine at first but quickly became a nightmare as my application scaled. Code became cluttered, and I was reinventing the wheel over and over again.
But then, the Smart vs Dumb Components concept finally clicked for me. Here's how I got there.
The Problem: Cluttered Components & Limited Reusability
In the past, I created a new component for almost every functionality. For example, if I had a dashboard with multiple boxes displaying different metrics, I would create a unique component for each metric box. While this worked, it wasn’t scalable. As the app grew, I ended up with too many components, each doing very specific things. This created unnecessary complexity and made it harder to manage.
But the real problem was reusability. I wasn't thinking about reusing components across different parts of the app. This meant that if I wanted to display similar data in another screen, I had to either build a new component or duplicate logic, both of which are poor practices.
The Shift: Smart vs Dumb Components
I soon discovered the power of smart and dumb components, which completely changed how I approached component design.
Smart Components are responsible for managing data, state, and logic. These components talk to services, handle API calls, and manage the state of the app.
Dumb Components are purely UI-focused and receive data via
@Input()
and send data via@Output()
. They don’t deal with business logic; their only job is to display data passed to them by smart components.
Example of Smart Component
Let’s say you have a user profile component. This component makes an API call to fetch user data and then passes the data to a dumb component that renders the profile UI.
// Smart Component: UserProfileComponent
export class UserProfileComponent implements OnInit {
userData: any;
constructor(private userService: UserService) {}
ngOnInit() {
this.userService.getUserProfile().subscribe(data => {
this.userData = data;
});
}
}
<app-user-profile-display [user]="userData"></app-user-profile-display>
Example of Dumb Component
The UserProfileDisplayComponent
is dumb because it only cares about displaying data, not fetching or managing it.
How I Implemented Smart vs Dumb Components
1. Using Route Data & Query Params for Reusability
When I was dealing with screens that needed to display different types of data (such as a list of users or products), I used Angular’s Route data and query parameters to make components more reusable.
Previously, I would create a new component for each screen or functionality, but with route data, I was able to pass configuration directly through the routing system. This allowed me to reuse the same component for different use cases, reducing code duplication and complexity.
For example, let’s say I had a list component that could display users, products, or orders depending on the route. Instead of creating three separate components, I used route data to determine what data to fetch:
// Route Configuration
const routes: Routes = [
{
path: 'users',
component: ListComponent,
data: { entityType: 'user' },
},
{
path: 'products',
component: ListComponent,
data: { entityType: 'product' },
},
];
In the ListComponent, I read the data
from the route to decide what to fetch:
// Smart Component: ListComponent
export class ListComponent implements OnInit {
entityType: string;
listData: any[];
constructor(private route: ActivatedRoute, private dataService: DataService) {}
ngOnInit() {
this.entityType = this.route.snapshot.data['entityType'];
this.fetchData();
}
fetchData() {
this.dataService.getData(this.entityType).subscribe(data => {
this.listData = data;
});
}
}
By using route data, I was able to create a single ListComponent that could serve multiple purposes, depending on the route configuration.
2. Shared Components for Simple UI Blocks
For components that are static or simple, such as a metric box in a dashboard, I made use of shared components. These are dumb components that only care about displaying data passed to them via @Input()
.
For example, a metric box showing a number:
// Dumb Component: MetricBoxComponent
export class MetricBoxComponent {
@Input() label: string;
@Input() value: string;
}
These components were designed to be generic and reusable across multiple screens. The key here is to think ahead about how you can reuse the component and what kind of inputs it will need.
Benefits I Saw from the Smart vs Dumb Approach
The benefits of switching to the smart/dumb component model were clear:
Cleaner Code – The separation of concerns made the code easier to understand and maintain.
Less Clutter – Using route data reduced the number of components I needed in a module, making the app more organized.
Better Reusability – I could easily reuse dumb components across multiple screens by passing different
@Input()
values.Easier Testing – Dumb components, being simple and focused on presentation, were much easier to test. Smart components, on the other hand, could focus on business logic and API calls without worrying about UI rendering.
Final Thoughts: My Rule of Thumb
Here’s how I decide whether a component should be smart or dumb:
If the component handles data fetching, state management, or business logic, it’s smart.
If the component is purely UI-focused, takes inputs via
@Input()
, and outputs events via@Output()
, it’s dumb.
For example:
A dashboard box displaying a metric is a dumb component.
A user profile component that fetches data from an API and passes it to a UI component is a smart component.
Wrapping Up
Switching to the smart/dumb component approach helped me design cleaner, more maintainable Angular apps. While it took time to fully grasp the concept and apply it, the improvements were well worth it. If you’re struggling with reusability, complexity, or clutter in your Angular apps, give this pattern a try.
What About You?
Do you use the smart/dumb component model in your Angular apps? What challenges have you faced? Let me know in the comments!
Subscribe to my newsletter
Read articles from Vishnu Kalanad directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
