Too many frontend tools

We Use Too Many Tools to Solve Simple UI Problems

Published:

We Use Too Many Tools to Solve Simple UI Problems

Modern frontend development has a strange problem: we have more tools than ever, yet building simple UI often feels harder, slower, and more fragile than it should be.

A button. A card. A layout. Things we solved years ago with plain HTML and CSS now require multiple libraries, configurations, and abstractions. Somewhere along the way, simplicity got buried under layers of tooling.

This article isn’t anti-tools — it’s about using the right level of complexity for the problem at hand. Let me show you what I mean.

When Simple UI Becomes Overengineered

Let’s take a very common example: a button. Just a clickable element that triggers an action.

Today, a “simple” button might involve:

All that infrastructure… for a clickable element.

<Button
  variant="primary"
  size="lg"
  intent="success"
  radius="md"
  elevation="2"
  leftIcon={<SaveIcon />}
  isLoading={isSubmitting}
>
  Save Changes
</Button>

Don’t get me wrong — this API is clean and convenient. But behind it sits an enormous amount of abstraction. The problem isn’t the button itself — it’s how many layers exist between intent and result.

The Hidden Cost

When you use this button, you’re importing:

// What you see
import { Button } from "@ui/components";

// What you're actually loading
import { ThemeProvider } from "@emotion/react";
import { useTheme } from "./theme-context";
import { cx } from "class-variance-authority";
import { forwardRef } from "react";
import { Spinner } from "./Spinner";
import { Icon } from "./Icon";
// ... and dozens more dependencies

For a button.

Tools Are Not Free

Every tool you add to your project introduces cost:

Mental Overhead

Every abstraction is another concept developers must understand. New team members don’t just learn React — they learn your component library’s API, your CSS-in-JS solution, your design token system, and your build configuration.

Onboarding Cost

I’ve seen talented developers spend days just understanding the architecture before writing a single line of productive code. “How do I change the color of this text?” shouldn’t require reading three documentation sites.

Maintenance Cost

Tools need updates. APIs change. Breaking changes happen. That button library you added? It now requires React 18, but your app is on 17. Time to refactor.

Upgrade Risk

What happens when the tool is abandoned? Or when the team pivots to a different solution? You’re left with technical debt and migration work.

The Worst Part?

We often add tools based on assumptions, not evidence:

Most projects never reach the complexity these tools were designed for.

The UI Layer Is Not the Backend

Here’s something that took me years to internalize: UI systems and backend systems have fundamentally different needs.

Backend systems often benefit from heavy abstraction:

These abstractions help manage real complexity: distributed systems, data consistency, security, and scale.

UI Is Different

The UI layer is inherently visual and declarative. It benefits from clarity over abstraction.

CSS already gives us:

But instead of mastering CSS, we wrap it in JavaScript and abstractions.

A Real Example

Here’s a button with plain CSS:

.btn {
  padding: 0.75rem 1.5rem;
  border-radius: 0.5rem;
  border: none;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn--primary {
  background: var(--color-primary);
  color: white;
}

.btn--primary:hover {
  background: var(--color-primary-dark);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.btn--primary:active {
  transform: translateY(0);
}

.btn--primary:disabled {
  background: var(--color-gray-300);
  cursor: not-allowed;
  transform: none;
}

And the HTML:

<button class="btn btn--primary">Save Changes</button>

This is not primitive. This is readable, debuggable, and predictable.

No build step needed. No runtime injection. No JavaScript for styling. Just CSS doing what it was designed to do.

When Tools Actually Make Sense

I’m not saying never use tools. I’m saying use them intentionally.

Tools are valuable when:

1. The Problem Repeats Many Times

If you’re building 50+ components that share behavior, a component library makes sense. But for 5 components? Probably overkill.

2. Rules Must Be Enforced Across Teams

Large teams benefit from constraints. A design system with tokens ensures consistency when dozens of developers are contributing.

3. Consistency Matters More Than Flexibility

If brand consistency is critical (think banking, healthcare), the constraints imposed by a design system are features, not bugs.

4. The System Is Designed, Not Improvised

Tools work best when you’ve already solved the problem manually and understand it deeply. If you’re still figuring out your component API, adding abstraction layers too early will hurt.

The Real Issue

The issue isn’t the tools themselves — it’s starting with tools instead of starting with understanding.

A Healthier Frontend Mindset

Here’s the mental framework I use before reaching for a new tool:

1. What Problem Are We Solving?

Be specific. “We need better styling” is vague. “We need consistent spacing across 200 components” is concrete.

2. Can Native CSS/HTML Solve This?

Modern CSS is incredibly powerful. Before adding a tool, try solving it with platform primitives. You might be surprised.

3. Will This Tool Reduce or Increase Cognitive Load?

Some tools simplify (ESLint catches bugs automatically). Others complicate (another config file to maintain). Be honest about which category your tool falls into.

4. What Happens If We Remove It in a Year?

“Reversibility” is underrated. How painful would it be to undo this decision? The more painful, the more carefully you should evaluate it.

5. Have We Tried the Simple Thing First?

Complexity should be earned through experience, not assumed upfront. Start simple. Add complexity only when you hit real limitations.

Practical Examples from Real Projects

Let me share two stories from projects I’ve worked on:

Project A: The Over-Tooled Dashboard

A client hired us to build an internal dashboard. Simple CRUD operations, data tables, forms. Nothing exotic.

The previous team had set up:

For an internal tool with 3 users.

The result? Developers spent more time fighting abstractions than building features. Bundle size was 800KB. Build time was 4 minutes.

Our solution? We rewrote it with plain React, basic CSS modules, and React Query for server state. Bundle dropped to 180KB. Build time: 30 seconds. Development velocity doubled.

Project B: The Under-Tooled Design System

A different client — a large enterprise with 50+ frontend developers across multiple teams. They had no shared components. Every team built their own buttons, inputs, modals.

The result? Wildly inconsistent UI. Designers couldn’t scale. Code duplication everywhere.

Our solution? We built a proper design system with a component library, design tokens, and Storybook for documentation. This increased complexity, but it was earned complexity. The problem was real, repeated, and costly.

What I’ve Learned

After years of building UI, here’s what I believe:

Start With the Platform

HTML and CSS are not “old” or “legacy” — they’re the foundation. Master them before abstracting them away.

Add Constraints Gradually

Don’t design for scale you don’t have. A 3-person team doesn’t need the same architecture as a 50-person team.

Optimize for Change, Not Perfection

Your first version will be wrong. Make it easy to change, not “perfect” upfront.

Sometimes, Boring Is Better

The most maintainable code I’ve written has been the most boring: straightforward HTML, vanilla CSS, minimal abstraction.

The Bottom Line

Frontend didn’t become harder because UI got more complex. It became harder because we over-solved problems that were already solved.

Better frontend isn’t about fewer tools — it’s about using tools intentionally.

Before you add that next library, ask yourself: am I solving a problem, or am I just following a trend?

Sometimes, the simplest solution is still the best one.

Code with purpose, build with intention.