DEV Community

Avisto
Avisto

Posted on • Originally published at Medium on

Your component is doing too much. Here’s how to fix that.

Front-end development today runs on components. The moment you’re building anything with real interactivity, they become the default answer and for good reason.

Yet for something so universally used, the concept stays surprisingly blurry. Everyone has their own definition, their own boundaries, their own way of using them. And that’s actually part of why they work so well the vagueness is a feature, not a bug. It leaves room for adaptation.

So this article won’t hand you a recipe or lock you into one specific implementation. The goal is simpler: give you the right questions to ask when you’re about to build a component. Complex systems are just simple systems stacked together. That’s all an application really is a collection of components, each doing its part.

This is the first piece in a series on front-end architecture. We’ll dig into two fundamental concepts that, once they click, will change the way you think about splitting up an interface: context and responsibility.

A component has a context

A component’s context is everything that implicitly defines it — its name, its place in the file tree, the tools around it, the way the code is written.

To understand why context matters, let’s take on two ideas that trip up a lot of developers.

Reusable and generic — the two holy grails of software

Forget the idea that a component needs to be generic. In most cases, chasing genericity actively makes your code worse. A component has its own identity, shaped by the project, the design, and the specific problem it solves.

Forget also the idea that a component must be reusable. There’s nothing wrong with building something you’ll only use once. That component still earns its place by isolating complex logic from the rest of the app.

The more important point — and the one that’s most often misunderstood — is the difference between generic and reusable.

A generic element is built to work everywhere. It answers no specific need and stays deliberately abstract to cover as many situations as possible. A reusable element, on the other hand, belongs to a context. It solves a specific problem, and happens to be useful in a handful of places.

The confusion comes from the fact that reusable elements get used multiple times, which makes them feel generic. But pushing a reusable element toward full genericity adds complexity, blurs its purpose, and makes it harder to maintain.

                                      Genericㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ Reusable
Answers a specific need ✗ ✓
Works in any context ✓ ✗
Easy to maintain ✗ ✓
Examples <Button>,<Modal> <ArticleCard>,<NavBar>
Enter fullscreen mode Exit fullscreen mode

Most of the time, a component lives inside a specific context a set of information that surrounds the technical task you’re solving. That context gives your code meaning and readability. The goal is to ground your component in reality, and surface as much useful information as possible through its name, its location, and its types.

Doesn’t a specific component create technical debt?

A common pushback is that contextualizing a component raises the cost of future changes. If it’s built for need A, what happens when need A becomes need B?

It sounds reasonable. But it usually isn’t. Let’s walk through an example.

Imagine a company building a blog. They’ve just hired a dev team and kicked off the project.


Need A: the site needs a horizontal navigation bar at the top of the page.

Julien, a junior developer, wants to do things right. He creates a navigation-bar component, and adds an isHorizontal prop so the bar can be flipped to vertical if needed. Smart, he thinks — this way the component can be reused across the project. He merges the code. From that day on, every developer who touches the nav bar is also responsible for maintaining a feature that adds zero value to the project.

A year later, the product shifts toward a dashboard layout. The nav bar needs to become a side menu. Since isHorizontal already exists, someone suggests keeping navigation-bar and just setting isHorizontal = false.


Need B: the site needs a side menu on the left.

What follows is a painful refactor. Yes, the prop was there but the project’s conventions have moved on, the framework has been updated, and the design barely resembles what it used to be. You’re forced to build something new out of something old.

The result: wrong HTML elements, orphaned CSS classes, dropdown logic that no longer belongs anywhere but nobody dares remove.

Now, what if Julien hadn’t tried to be clever?

If he’d named his component top-nav-bar and skipped the isHorizontal prop entirely, the answer to need B would have been obvious: create a side-menu. No patching, no backwards compatibility, no baggage. Both components live side by side, each doing their own thing, and removing one doesn't touch the other.

Keeping a component focused on one thing keeps it manageable. The clearer its intent, the easier it is to evolve. Context doesn’t constrain your architecture it guides it.

A component has a responsibility

Responsibility is the second pillar. A component should be a coherent unit of knowledge and behavior something that can stand on its own and fulfil a clear mission. If you know the Single Responsibility Principle, you already know the idea. One component, one job.

Back to Julien. He’s now asked to build cards for displaying articles a title, an image, and a read button. He handles it cleanly and creates a card component.

Two months later, a new article format appears. These ones have a banner and an icon, plus a tag system to tell them apart. Julien updates card. It now handles multiple article types.

Six months after that, the company launches a podcast section. The cards look similar, but now they need to play audio. Julien figures he’ll reuse card for that too.

And that’s where things fall apart.

Responsibilities pile up. Context evaporates. The component keeps absorbing new features until it crosses 500 lines and becomes a bug factory. Fix something for articles, break something for podcasts. Nobody knows what belongs to what anymore.

The best way to avoid this is to not let it start. The fix here isn’t technical it’s a naming decision. It’s more intuitive to say “a podcast isn’t an article” than “a podcast doesn’t belong in a card.” So the natural solution is an article-card that keeps handling articles, and a new podcast-card built from scratch for podcasts.

Context and responsibility related, but not the same

Context asks: where does this component live, and why does it exist? Responsibility asks: what does it actually do?

A component can have a clear context article-card and still have too many responsibilities, if it's handling display, analytics tracking, and audio playback all at once.

And a generic card component can have a single, clean responsibility render content while being completely context-free, which means nobody knows what content, for whom, or in what situation.

Two things to take away

So, what makes a good component? Simply one you can read, change, or delete without hesitation. You open it and immediately know what it’s for. It holds up over time and tells you something about itself just by existing through its name, its location, its types, all reflecting the need it was built to serve. And it does one thing. One clear mission you could write on a Post-it.

Of course, neither of these are hard rules. They’re questions to keep in your back pocket. Does this component answer a specific need? Does it do one thing? If the answer is yes, you’re on the right track.

In the next article, we’ll go further with the container/presentational pattern. Good components are only half the story. The other half is knowing how to organize them.

Top comments (0)