September 11, 2025

Advanced

Generic Components: The Art of Not Reinventing the Wheel (Part 2)

In this part, we're going to find out when to use and when NOT to use generic components, including the famous case of the generic filters that went completely wrong. We'll also explore specific cases where generalization can be problematic.

Recap of Part 1

In part 1 about Generic Components: The Art of Not Reinventing the Wheel, we explored the fundamentals of generic components:

  • What they are: reusable tools that solve specific problems, not formal computer science concepts.

  • Why use them: DRY (don't repeat code), makes maintenance easier, ensures visual consistency.

  • Practical examples: We saw how to turn duplicated buttons and inputs into generic components in React and Angular.

💡 If you haven't read Part 1, click here to read it first and understand the full context before continuing.

When to Use Generic Components?

Use them when:

1. You have patterns that repeat across various parts of the application.

This is the most obvious one, but it's important to understand what "pattern" means. It's not just about visual appearance, it's about behavior, structure, and logic. If you're writing the same email validation in 5 different places, it's time to create a generic component.

But be careful: don't confuse "pattern" with "coincidence". If two components look similar but have fundamentally different purposes, forcing a generalization can create more problems than it solves.

2. You want to maintain visual and behavioral consistency.

Consistency isn't just aesthetic. It affects the user experience and the maintainability of the code. If you want all confirmation buttons to behave the same way, a generic component is the solution.

3. You need to make future maintenance easier.

Think about it: in the future, when you need to change something, would you rather change it in one place or in 20? Generic components are an investment in the future of your code.

4. You're building a design system.

If you're creating a component library for your team or company, generic components are essential. They ensure that everyone follows the same standards.

Don't use them when:

1. The component is used only once.

Don't force generalization. If something is truly unique and specific, leave it as is. Generic components are tools, not absolute rules.

2. The logic is so specific that it doesn't make sense to generalize.

Sometimes, trying to create a generic component for something very specific can result in a confusing component that's hard to use. It's better to have a specific, well-defined component than a confusing generic one.

3. You're creating a "Frankenstein" component that tries to do everything.

This is a common mistake. A component that has 50 different props and tries to solve every problem in the world ends up being hard to use and maintain. Sometimes it's better to have several smaller, specific components.

The Case of the Generic Filters: An Example of What NOT to Do

Here's a real-world experience that perfectly illustrates when NOT to use generic components. In a system I worked on, there was a generic filter that tried to solve every filtering problem in the world. The result was a disaster.

The generic filter had:

  • Generic filtering logic for all data types;
  • Management of dependencies between fields;
  • Automatic HTTP requests;
  • Generic validation;
  • Shared global state.

🚨 The problems that arose:

Excessive complexity: The component had 30+ props and was impossible to understand.

Tight coupling: changing one filter affected other filters on other screens.

Hellish debugging: when something went wrong, it was hard to track down the problem.

Poor performance: The component made unnecessary requests.

Impossible maintenance: nobody wanted to touch the code for fear of breaking everything.

The correct solution:

Instead of a generic filter, we created generic filter elements:

// ✅ Generic elements that combine together
<FilterContainer>
  <FilterInput
    label="Nome"
    placeholder="Digite o nome..."
    value={filters.nome}
    onChange={(value) => setFilters({ ...filters, nome: value })}
  />

  <FilterSelect
    label="Categoria"
    options={categories}
    value={filters.categoria}
    onChange={(value) => setFilters({ ...filters, categoria: value })}
  />

  <FilterDateRange
    label="Período"
    startDate={filters.dataInicio}
    endDate={filters.dataFim}
    onChange={(start, end) =>
      setFilters({ ...filters, dataInicio: start, dataFim: end })
    }
  />

  <FilterActions>
    <Button onClick={handleFilter}>Filtrar</Button>
    <Button variant="secondary" onClick={handleClear}>
      Limpar
    </Button>
  </FilterActions>
</FilterContainer>

🎯 Why this approach is better:

Single responsibility: each element does one thing well.

Natural composition: the elements combine to create complex filters.

Flexibility: each filter can have its own specific logic.

Maintainability: changes in one element don't affect others.

Testability: each element can be tested independently.

When NOT to Use Generic Components

1. Complex Business Logic.

If the component has specific business logic (such as complex validation rules, specific calculations, or integrations with specific APIs), it probably shouldn't be generic.

A bad example:

// ❌ Trying to make generic something that is specific
<BusinessLogicComponent
  businessRules={complexRules}
  apiEndpoints={endpoints}
  validationSchema={schema}
  calculationEngine={engine}
/>

Now, let's see a good example of how we could do this:

// ✅ Specific components for each case
<UserRegistrationForm />
<ProductCatalogFilters />
<OrderCalculationWidget />

2. Very Specific States

If the component manages a state that is unique to a specific context, it shouldn't be generic.

A bad example:

// ❌ Specific state in a generic component
<GenericForm
  state={userState}
  setState={setUserState}
  validation={userValidation}
  apiCall={updateUser}
/>

Again, let's see a good example of how we could do this:

// ✅ Specific state in a specific component
<UserProfileForm />

3. Complex External Dependencies

If the component depends on many external libraries or has many dependencies, it may not be a good candidate for generalization.

Conclusion: The Balance Between Flexibility and Simplicity

Generic components are like a double-edged sword: when used well, they're powerful tools that save time and ensure consistency. When used poorly, they become monsters that complicate more than they help.

The most important lesson of this part:

  • Generalize what is truly common: buttons, inputs, basic modals.
  • Keep specific what is unique: business logic, complex states, specialized filters.
  • Prefer composition over configuration: several small components that combine together are better than one giant component with 50 props.

The case of the generic filters taught us that less is more. Instead of trying to create a component that solves everything, it's better to create components that do one thing well and combine naturally.

Remember: generic components should make your life simpler, not more complicated. If you're spending more time configuring the generic component than it would take to create something specific, you're doing it wrong.

In the next part, we'll talk about the best practices for creating effective generic components and a practical example of a generic modal. Don't miss it!

Post Author

Leonardo Henrique

Leonardo Henrique

E aí! Sou o Leonardo Henrique, mas muita gente me conhece como "Leozinho do Front" — culpa da minha paixão por Front-End. Trabalho como desenvolvedor Full Stack, com bastante experiência em Angular, um carinho especial por React e, agora, me aventurando com Vue. Curto demais jogos e animes, e um dia ainda quero me aventurar na área de games.