Back
June 11, 2026Frontend

The same button component in 3 major versions of VueJS

Vue.js has come a long way since its initial release. From its humble beginnings as a lightweight library inspired by Angular and React, it has grown into one of the most beloved frontend frameworks. In this post, I'll look at three major milestones: Vue 1, Vue 2 (the Options API era that powered massive adoption), and Vue 3 (the modern Composition API default). I'll implement the same simple feature: a button that shows an alert when clicked.

Vue 1 (2015)

Vue 1 introduced the core concepts that made it stand out: reactive data binding, a simple template syntax, and component-based architecture using Vue.extend() and global registration.

Key characteristics of this era:

  • Heavy use of Vue.extend() for component definitions.
  • Templates were often strings or in the DOM.
  • Event handling with v-on (shorthand @ came later but was similar).
  • Data was defined directly, with caveats around sharing objects.

Example with Vue 1

<html>
  <head>
    <script src="https://unpkg.com/vue@1.0.28/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <alert-button></alert-button>
    </div>

    <script>
      var AlertButton = Vue.extend({
        template: '<button v-on:click="showAlert">Click me</button>',
        methods: {
          showAlert: function() {
            alert('Hello!');
          }
        }
      });

      Vue.component('alert-button', AlertButton);

      new Vue({
        el: '#app'
      });
    </script>
  </body>
</html>

What's notable: Everything was quite imperative and constructor-based. Components felt like enhanced custom elements. This version was lightweight and easy to pick up, focusing on progressive enhancement. No build tools were strictly required.

Vue 2: The Options API (2016–2020)

Vue 2 refined the developer experience significantly. It popularized the Options API: organizing code into clear sections like data, methods, computed, and lifecycle hooks. This became the standard way most developers learned and used Vue for years. Global Vue.component was still common, but Single-File Components (.vue files) with webpack/Vue CLI became the norm.

Key improvements:

  • More structured component options.
  • Better reactivity system.
  • Official CLI and excellent documentation.
  • v-on:click shorthand @click widely used.

Example with Vue 2

<template>
  <button @click="showAlert">
    Click me
  </button>
</template>

<script>
export default {
  name: 'AlertButton',
  methods: {
    showAlert() {
      alert('Hello from Vue 2.x Options API!');
    }
  }
}
</script>

Usage in parent:

<template>
  <div>
    <AlertButton />
  </div>
</template>

<script>
import AlertButton from './AlertButton.vue'

export default {
  components: { AlertButton }
}
</script>

This syntax feels very organized and explicit. The separation of concerns (template, script, style) in Single-File Components made large apps much more maintainable.

Vue 3: Composition API & <script setup> (2020+)

Vue 3 brought a complete rewrite with better performance, TypeScript support, and the Composition API. While the Options API is still fully supported for backward compatibility, the new <script setup> syntax has become the recommended default for its conciseness and better logical composition.

Key differences in Vue 3:

  • Composition API allows grouping logic by feature rather than option type.
  • <script setup> is syntactic sugar that compiles to highly optimized code.
  • Better tree-shaking and smaller bundle sizes.
  • defineProps / defineEmits macros for better ergonomics.

Example with <script setup>

<script setup>
const showAlert = () => {
  alert('Hello from Vue 3.x Composition API!');
}
</script>

<template>
  <button @click="showAlert">
    Click me
  </button>
</template>

Evolution Summary

  • Vue 1: Focused on simplicity and getting started quickly with minimal ceremony.
  • Vue 2: Emphasized structure and scalability through the Options API.
  • Vue 3: Prioritizes flexibility, performance, and composability. Logic can now be extracted into reusable composables (useAlert.js, etc.), making code more modular.

The button itself didn't change much, what changed is how we organize that functionality, and it evolved dramatically for the better.

In any case, it's impressive how the framework has stayed true to its roots: approachable, performant, and friendly to not-so-bright people like me.

I also talk about web dev here: