Development

Vue in 2026: What Changed and What Only Looks Like It Did

Composition API, Nuxt 3, and the Vue ecosystem explained from scratch

The Options API is not deprecated, script setup is the real default, Vuex is finished and Vue 2 has had no patches since the end of 2023. A catch-up on what moved, and the reactivity trap that still catches everyone.

If your last Vue project was a couple of years ago, most of what you know still works and a few load-bearing things have moved underneath it. This is a catch-up rather than a beginner's tour: what changed, what was quietly retired, and which of the choices that used to matter no longer do.

The one that is not optional

Vue 2 reached end of life on 31 December 2023. No patches, no security fixes, no compatibility guarantees from the ecosystem. If you are still on it, that is the whole story and everything below is theory until it is dealt with.

The path out is @vue/compat, a build of Vue 3 that accepts most Vue 2 behaviour and warns about each incompatibility as you hit it. You migrate incrementally with a running application rather than in one commit. Commercial extended support for Vue 2 exists, and it buys time rather than solving the problem.

The breaking changes that actually cause work are a short list: the global API moved from new Vue() to createApp(), filters were removed entirely, v-model on components changed its prop and event names, and functional components are now plain functions. Most codebases spend their migration time on third-party libraries that never shipped a Vue 3 version.

What changed, and what did not

A status table for seven parts of the Vue ecosystem in 2026. Vue 2 is end of life with no patches since 31 December 2023. The Options API is fully supported and not deprecated. The Composition API is for logic reuse between components. Script setup is the default and is compile-time sugar rather than a third API. Vuex is maintenance only, replaced by Pinia. Vue CLI is retired in favour of Vite. Vapor Mode compiles to direct DOM operations, is opt-in per component, and its status should be checked against the documentation.
Only the first row is urgent. The second is the one people get wrong — rewriting working Options components into the Composition API buys nothing.

The single most common misconception is worth stating plainly: the Options API is not deprecated. It is fully supported, it is not going anywhere, and for a component that holds a bit of state and a couple of methods it is often the clearer thing to read. Anyone telling you to rewrite working Options components into the Composition API is giving you a refactor with no payoff.

The reason the Composition API exists is logic reuse. When two components need the same stateful behaviour — a fetch with loading and error state, a keyboard shortcut, a resize observer — the Options API makes you reach for mixins, which collide silently and hide where a property came from. Composables solve exactly that and nothing else.

javascript
// composables/useFetch.js — the thing mixins were always trying to be
import { ref, watchEffect, toValue } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)
  const loading = ref(false)

  watchEffect(async (onCleanup) => {
    const controller = new AbortController()
    onCleanup(() => controller.abort())   // cancels when url changes or unmounts

    loading.value = true
    error.value = null
    try {
      const res = await fetch(toValue(url), { signal: controller.signal })
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      data.value = await res.json()
    } catch (e) {
      if (e.name !== 'AbortError') error.value = e
    } finally {
      loading.value = false
    }
  })

  return { data, error, loading }
}

toValue() is the detail that makes a composable reusable: it accepts a plain string, a ref or a getter, so callers can pass whatever they have and reactivity still works. onCleanup is what stops a slow response from a previous URL overwriting a newer one — the race condition that quietly affects a large share of hand-written fetch code.

script setup is the actual default now

<script setup> is not a third API. It is compile-time sugar over the Composition API that removes the setup() function and the return statement, and it is what new Vue code looks like.

html
<script setup lang="ts">
import { computed } from 'vue'

// Types come straight from the generic — no runtime prop definitions
const props = defineProps<{ userId: string; compact?: boolean }>()

// defineModel collapses the prop + emit + watcher dance into one line
const search = defineModel<string>()

const label = computed(() => props.compact ? 'Details' : 'Full details')
</script>

<template>
  <input v-model="search" :placeholder="label" />
</template>

defineModel() is the one to notice if you have been away. Two-way binding on a component used to mean declaring a prop, declaring an emit, and writing a computed with a getter and setter to bridge them. It is now one line, and the parent's v-model works exactly as it did.

The reactivity trap that still catches everyone

This has not changed and it is still the most common source of "why is my UI not updating".

javascript
import { reactive, toRefs } from 'vue'

const state = reactive({ count: 0, name: 'Ada' })

// BROKEN — destructuring copies the values out and the link is gone
const { count } = state
count++                      // updates a local number, not the UI

// FINE — toRefs keeps each property connected
const { count: countRef } = toRefs(state)
countRef.value++             // updates the UI

The practical rule most teams settle on is to use ref() for almost everything and reserve reactive() for objects you will never destructure. ref() costs you .value in script — and nothing at all in templates, where it is unwrapped automatically — and in exchange it never silently loses its connection.

The ecosystem, with the retirements marked

  • Pinia replaced Vuex. Vuex is in maintenance and will not gain features. Pinia has no mutations, works with the Composition API, and types itself properly without the declaration gymnastics Vuex needed. A new project using Vuex in 2026 is a new project starting on a dead dependency.
  • Vite is the build tool. Vue CLI is retired. If you are on it, migrating is usually an afternoon and the dev server difference is not subtle.
  • Vue Router 4 for Vue 3, with createRouter and createWebHistory replacing the old constructor form.
  • Vitest for unit tests, sharing your Vite config so there is no second build pipeline to keep in sync. Vue Test Utils still exists; Testing Library sits on top of it and pushes you toward testing what a user sees rather than component internals.
  • Nuxt when you need the server. Server-side rendering, file-based routing, API routes, SEO metadata. Plain Vite plus Vue remains the right answer for an application behind a login, where none of that earns its complexity.

Where the framework is going

The interesting development is Vapor Mode: a compilation strategy that produces direct DOM operations and skips the virtual DOM entirely, in the manner of Solid. It is designed to be opt-in per component and to interoperate with normal components in the same application, so it is an optimisation you reach for rather than a rewrite you commit to.

Check its current status against the Vue documentation rather than any article, this one included — it has been progressing for some time and the details of what is supported have moved more than once. The direction is stable even where the specifics are not: less runtime, more compiler.

Starting a new project today

bash
npm create vue@latest

Take TypeScript, take Vue Router if you have more than one page, take Pinia if state outlives a single component tree, take Vitest. Skip anything you are unsure about — all of them are straightforward to add later, and a scaffold full of tools nobody uses is its own kind of debt.

Then write your first few components in whichever API reads better to you, and reach for composables at the point you find yourself copying stateful logic between two of them. That is the moment the Composition API pays for itself, and before it, it mostly does not.

Versions and scope

Version-specific details above reflect Vue 3.4 and later; defineModel in particular is not available on earlier releases. Vapor Mode's status and supported feature set change, so verify against the official documentation before planning around it.

The Vue 2 end-of-life date is as published by the Vue team. We have not benchmarked any of the rendering strategies discussed and no performance figures here are our own.

vuejscomposition-apiscript-setuppiniavitejavascriptfrontend

Arslan ud Din Shafiq

Founder and lead editor of LearnCybers. Full-stack engineer with expertise in Linux systems, cybersecurity, cloud infrastructure and web development. Writing about practical technology since 2019.

Related reading

Newsletter

Get smarter about security

Practical guides, tooling notes and the developments actually worth your attention — delivered when there is something worth saying.

No spam. Unsubscribe in one click.