DEV Community

Cover image for When Goliath Fell to David’s Smallest Stone: The Innocent Dropdown Tweak That Shattered React, Vue, and Svelte
Ryo Suwito
Ryo Suwito

Posted on

1

When Goliath Fell to David’s Smallest Stone: The Innocent Dropdown Tweak That Shattered React, Vue, and Svelte

Once Upon a Time, Chaos Reigned in Front-End Development

The front-end world has been ruled by frameworks that promised you freedom:

  • React, the "build anything, but at what cost?" framework.
  • Vue, the "everything is kinda okay but just try to reuse your logic, I dare you" framework.
  • Svelte, the "we’re simple until you need inheritance, then good luck" framework.

For years, developers convinced themselves these tools were the pinnacle of modern engineering. But behind the promises of "flexibility" and "developer experience" lurked frustration, duplication, and tears.


The Challenge: A Button with a Dropdown

Let’s test these frameworks—and Neuer—on a simple task:

A button with a dropdown.

The Use Cases:

  1. Version 1: Create a button with a dropdown.
  2. Version 2: Customize the dropdown’s content.
  3. Version 3: Override the click behavior.
  4. Version 4: Change styles to match a new context.

Sounds easy, right? Watch the big three fall apart. 🎭


React: A Render Function and a Prayer

React devs, gather ‘round. Here’s your React-based dropdown:

const DropdownButton = () => {
  const [open, setOpen] = React.useState(false);
  return (
    <div className="dropdown">
      <button className="btn" onClick={() => setOpen(!open)}>
        Click Me <span className="caret"></span>
      </button>
      <div className={`dropdown-content ${open ? "show" : ""}`}>
        Dropdown content
      </div>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

At first glance:

"Wow, React is so clean! Look at that JSX!"

But now let’s tweak it.


Version 2: Customize Dropdown Content

React says: "Time to rewrite everything!"

const CustomDropdownButton = () => {
  const [open, setOpen] = React.useState(false);
  return (
    <div className="dropdown">
      <button className="btn" onClick={() => setOpen(!open)}>
        Click Me <span className="caret"></span>
      </button>
      <div className={`dropdown-content custom-style`}>
        Totally custom content!
      </div>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Duplicated logic, duplicated state.

Hope you remembered to copy the setOpen function, or you’ll break everything. 🙃


Version 3: Override the Click Behavior

React says: "Sure, but you’ll need to rewrite the whole component again."

const CustomClickDropdown = () => {
  const [open, setOpen] = React.useState(false);
  const customToggle = () => console.log("Custom click logic!");
  return (
    <div className="dropdown">
      <button className="btn" onClick={customToggle}>
        Click Me <span className="caret"></span>
      </button>
      <div className={`dropdown-content ${open ? "show" : ""}`}>Dropdown content</div>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Fun Fact: That setOpen logic? Yeah, it’s gone now. Good luck handling state without rewriting it all. 😏


Version 4: Change Styles

React says: "Hope you enjoy inline styles or CSS modules!"

const StyledDropdownButton = () => {
  const [open, setOpen] = React.useState(false);
  return (
    <div className="dropdown" style={{ backgroundColor: "lightblue" }}>
      <button className="btn" onClick={() => setOpen(!open)}>
        Click Me <span className="caret"></span>
      </button>
      <div className={`dropdown-content ${open ? "show" : ""}`}>Dropdown content</div>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Inline styles? Really? Are we in 2013? React: where modularity goes to die. 💀


Vue: Directives All Day, Every Day

Here’s Vue’s starting dropdown:

<template>
  <div class="dropdown">
    <button class="btn" @click="toggleDropdown">
      Click Me <span class="caret"></span>
    </button>
    <div :class="['dropdown-content', { show: isOpen }]">Dropdown content</div>
  </div>
</template>

<script>
export default {
  data() {
    return { isOpen: false };
  },
  methods: {
    toggleDropdown() {
      this.isOpen = !this.isOpen;
    },
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Looks nice! But let’s see the cracks.


Version 2: Customize Dropdown Content

Vue says: "Go ahead and duplicate everything."

<template>
  <div class="dropdown">
    <button class="btn" @click="toggleDropdown">
      Click Me <span class="caret"></span>
    </button>
    <div class="dropdown-content custom-style">Totally custom content!</div>
  </div>
</template>

<script>
export default {
  data() {
    return { isOpen: false };
  },
  methods: {
    toggleDropdown() {
      this.isOpen = !this.isOpen;
    },
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Another entire component for one small tweak. DRY? What’s that? 🥴


Version 3: Override the Click Behavior

Vue says: "Rewrite the methods section too, kid."

<script>
export default {
  data() {
    return { isOpen: false };
  },
  methods: {
    toggleDropdown() {
      console.log("Custom click logic!");
    },
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

Your dropdown’s behavior is isolated from the base logic. Hope you don’t need to share any logic with other components.


Version 4: Change Styles

Vue says: "Scoped styles to the rescue!"

But you still need to copy the template and styles into a new file.


Svelte: Rewrite It All, Forever

In Svelte, you get simplicity… until you need to reuse or tweak something.


The Neuer Way: One Class to Rule Them All

Version 2: Customize Dropdown Content

Extend. Override get template. Done.

export class CustomDropdown extends DropdownButton {
    static {
        this.registerElement();
    }
    get template() {
        return `
            <div class="dropdown">
                <button class="btn" c-click="toggleDropdown">Click Me <span class="caret"></span></button>
                <div class="dropdown-content custom-style">Totally custom!</div>
            </div>
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

Version 3: Override Click Behavior

Override one method. Done.

export class CustomClickDropdown extends DropdownButton {
    static {
        this.registerElement();
    }
    toggleDropdown() {
        console.log("Custom click logic!");
    }
}
Enter fullscreen mode Exit fullscreen mode

Version 4: Change Styles

Tweak get styles. Done.

export class StyledDropdown extends DropdownButton {
    static {
        this.registerElement();
    }
    get styles() {
        return `
            ${super.styles}
            .dropdown-content {
                background-color: lightblue;
                color: darkblue;
            }
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

The Moral of the Story

React makes you duplicate.

Vue makes you rewrite.

Svelte makes you start over.

Neuer? Makes you smile. 😏

Stop copying, pasting, and rewriting your life away. It’s time for a Neuer way of doing things.

Welcome to the future.

Image of AssemblyAI

Automatic Speech Recognition with AssemblyAI

Experience near-human accuracy, low-latency performance, and advanced Speech AI capabilities with AssemblyAI's Speech-to-Text API. Sign up today and get $50 in API credit. No credit card required.

Try the API

Top comments (9)

Collapse
 
webjose profile image
José Pablo Ramírez Vargas • Edited

This is such a try-hard article trying to sell Neuer.

  • React can render children, and Vue has slots.
  • React can get a callback prop and have it called from the click handler. Vue probably can too.
  • React sucks at CSS (and many other things). There's no defense there.

But bro, what happened with the Svelte component? Afraid much?

<script lang="ts">
    import type { Snippet } from "svelte";
    import type { HTMLAttributes } from "svelte/elements";

    type Props = & HTMLAttributes<HTMLDivElement> & {
        onClick?: () => void;
        children?: Snippet;
    }

    let {
        onClick,
        children,
        ...restProps
    }: Props = $props();

    let open = $state(false);

    function onclick() {
        open = !open;
        onClick?.();
    }
</script>

<div class="dropdown-container">
    <button type="button" {onclick}>Open <span class="caret"></span></button>
    {#if open}
        <div class="dropdown" {...restProps}>
            {@render children?.()}
        </div>
    {/if}
</div>
Enter fullscreen mode Exit fullscreen mode

You get full Intellisense:

Image description

And this is how it looks like being used:

<Dropdown onClick={() => {console.log('There was a click.')}} class="custom-class">
    Custom Dropdown Content!
</Dropdown>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
crenshinibon profile image
Dirk Porsche

Where are the Svelte examples ... did you realize it isn't that bad at all? Inheritance and early abstraction is bad ... code duplication is good, for a while. Abstract when it makes sense. And then you can make those components easily customizable via props ...

Collapse
 
ryo_suwito profile image
Ryo Suwito

Thank you for sharing your thoughts. This example isn’t about over-abstraction—it’s about Separation of concerns by design.

Of course, a button dropdown is a simplified example. A more compelling real-life use case is something like a GraphChartComponent.

Imagine the component autonomously pulls its data via an API.
Now, you need a DonutChart? Sure, add a prop.
Then you want it to use a different base URL endpoint? Another prop.
Now you want an "Export as PNG" button? Another prop—except the original component doesn’t even need this button.

With this props-heavy approach, your once-focused GraphChartComponent turns into a bloated, overly generic component, crammed with features it doesn’t always need.

Instead, using inheritance, you could create a DonutChart or ExportableGraphChart as subclasses, each handling their specific features while reusing the shared logic of the parent. This keeps components modular, clean, and tailored to their purpose without unnecessary complexity.

It’s not about controlling everything via props—it’s about creating adaptable components that are easy to extend. Hope that clears things up!

Collapse
 
crenshinibon profile image
Dirk Porsche • Edited

In my distant past, I had a similar perspective, when doing Java stuff, this was basically how you would approach this. But this falls down pretty quickly, believe me (or not, idk). In Java-Land they came up with Dependency-Injecting every "Trait", to get themselves out of the inheritance mess; ... which made it even worse.

Just don't do it. There is nothing bad in rewriting simple things from time to time.

And if you feel the pain of copying code over and over and having to make changes everywhere... make a configurable base component (GraphChartComponent; single point of contact with the wrapped chart library; with just the props you need) and wrap it with specific components (DonutChart, ExportableChart) that do the plumbing (setting default props in the wrapped Component) for you.

No inheritance needed. More flexible. Easier to reason about.

When you come to the point where you have a monolithic BaseComponent exposing all the features of the underlying GraphLibrary (in your example), then you have done the work for them and created a Svelte/React/Vue Component of the ChartLibrary. You might want to gift it to them.

But if you then have the feeling, this is a mess, well, then you should think about using this Library at all; under the hood it looks the same.

Thread Thread
 
ryo_suwito profile image
Ryo Suwito

Coming from a background in Java and backend development, I've encountered both the strengths and pitfalls of object-oriented programming (OOP) over time.

The key difference, I believe, lies in how changes are introduced and the ripple effects they cause. (And oh gosh, don’t even get me started on the pain of cramming endless configurations into a constructor's mouth—ugh 😂)

When leaning heavily on composition and props, any change often means adding new props or tweaking the base components. This creates a cascading effect: instead of testing a single modification, you now need to regression test the base component and every descendant relying on it.

Contrast this with a clean separation of concerns and direct overriding, where changes are localized and the primary focus stays on the specific functionality being adjusted. The ripple effects are minimized, and you aren’t constantly dealing with regressions.

The props-based approach has its downsides too—it can quickly spiral into chaos. Conditional rendering, hooks, data transformations, and edge cases pile up, leaving you with a bloated JSX monster stretching hundreds of lines. At some point, maintaining or extending such a component becomes so overwhelming that you might find yourself rewriting it from scratch.

And that’s the real kicker: why toss out perfectly good, QA-approved, battle-tested components just to reinvent the wheel for every new use case? Direct overriding offers a more predictable path to adaptation while preserving the integrity of your core components.

Anyway, different domains have different needs, and at the end of the day, it’s all about finding what works best for your context. 😊

Collapse
 
abo_hassan_1a298d9ca0eda3 profile image
Abo Hassan • Edited

Are we pretending slots don't exist in Vue?

And regarding overriding @click, you are correct it's not possible. However, solutions will always look different in each framework. That's not necessarily worse. In this case, you could use a prop called by @click with default behavior. A few extra lines, sure, but not tedious at all and I'd definitely prefer that above writing it the Neuer way with a JSX-looking syntax. Not discrediting Neuer, it's just my personal preference.

And let's not pretend you've not chosen the scenarios where Neuer will shine, surely the other frameworks/libraries will beat Neuer in other regards. It's quite vain to present Neuer as the new Godsend framework-destroyer to save frontend development once and for all. I absolutely appreciate it as an alternative which may have a future among frontend frameworks. Respecting your hard work, and please do make comparisons, but be fair about it and also dare acknowledge the weaknesses of the framework wherever they may be. People deserve to see pros and cons.

Collapse
 
ryo_suwito profile image
Ryo Suwito • Edited

Hi! Thank you for sharing your thoughts!

I’m sure every solution has its pros and cons, so I’ll let you decide for yourself what the cons of Neuer might be. Fair enough? 😊

Here’s the Simplest Example

Take a look at this basic implementation of a Neuer-component:

Basic Neuer-component Example

Here’s a Basic To-Do List Example

If you want something a bit more practical, check out this basic to-do list built with Neuer:

Basic To-Do List Example

How to Use These Components

Using Neuer components is super simple! Here’s an example:

How to Use Neuer Components

Neuer: My Baby

Neuer is my baby, so it might not be perfect—but that’s okay! Just in case you want to take a closer look or explore more, here’s the GitHub repo:

Neuer GitHub Repo

I just start with the pros, and you find the cons okay?

No build times.

No assumptions.

Just tools to help you build components yourself, your way.

Collapse
 
ezekiel_77 profile image
Ezekiel

No link to Neuer?
Is it a framework?

Collapse
 
ryo_suwito profile image
Ryo Suwito • Edited

Ezekiel, You Just Asked the Million-Dollar Question!

Is Neuer a framework? HAHAHA, not even close, champ! Neuer is the anti-framework, the liberator, the toolset for devs.

What’s Neuer?

  • No Build Times: Write it, use it. Neuer doesn’t waste your time with a build pipeline.
  • No Assumptions: It doesn’t force you into patterns or dictate how you should structure your app.
  • Just Tools: Want a modular router? Done. Need scoped state? It’s built-in. Every component is modular, lightweight, and genius.

Wanna See What Neuer Can Do?

Check out these links and prepare to have your mind blown:

The Simplest Neuer Example:

Basic Neuer-router Example

A To-Do List (Yes, It’s That Easy):

Basic To-Do List Example

How to Use It:

How to Use Neuer Components

And if you’re the curious type, here’s the repo where the magic lives:

Neuer GitHub Repo

So Ezekiel, is Neuer a framework?

Nope. It’s freedom.

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay