mrkeyoor.com_
Sat 19 Sept 06:44 UTC
npmWeb Frontendupdated 19 Sept 2026

vue review

Vue 3.5.41 is a component framework for browser interfaces. Single-file components place an HTML-like template beside JavaScript or TypeScript and optional scoped CSS. `ref`, `reactive`, `computed`, and watchers update the DOM through the component renderer, while directives such as `v-if`, `v-for`, and `v-model` keep common interactions in templates. The package includes the runtime, template compiler, single-file-component compiler, and server renderer; routing and application state live in separate packages. Version 3.5.41 is a corrective release for SSR hydration, async custom elements, very large scheduler queues, Teleport transitions, stable-list lifecycle hooks, and `defineModel` inference. Our whole-package bundle measured 47.8 KB gzipped before application code.

Verdict

Vue 3.5.41 installed in 4.9 seconds, occupied 19 MB, passed npm audit with 0 findings, and bundled at 47.8 KB gzipped in our whole-package probe. It fits teams that want template-led components and Vue's surrounding tools; skip it for a static page or when a hard React dependency already dictates the stack.

We installed it

Lab card: what happened when we installed vueScreenshot of vue documentation
Install✓ · 4.9s30 packages on disk · 19 MB
ImportESM import works · require() works · CommonJS package with exports map
Browser47.8 KBgzipped (122.5 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does vue install cleanly?

Yes. In a fresh container with an empty cache, npm install vue finished in 5 seconds, leaving 30 packages and 19 MB on disk. npm audit reported no known vulnerabilities.

How much does vue add to a browser bundle?

47.8 KB gzipped (122.5 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does vue work with both ESM and CommonJS?

Yes. Both import 'vue' and require('vue') worked in Node 22 in our run. The package is published as CommonJS with an exports map.

Does vue include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

vue or react: which should you use?

react: Use it when required libraries, React Native, or the team's existing component investment decides the ecosystem. Vue 3.5.41 installed in 4.9 seconds, occupied 19 MB, passed npm audit with 0 findings, and bundled at 47.8 KB gzipped in our whole-package probe.

When should you not use vue?

A required component library, SDK, hiring pipeline, or native-mobile application is React-only. An adapter or second frontend stack can cost more than the framework preference saves.

API stability5/5Vue 3's component contract, template directives, refs, computed values, lifecycle hooks, and Composition API have stayed recognizable through the 3.x line. Release 3.5.41 corrects edge behavior rather than introducing a migration: it preserves lifecycle hooks in stable `v-for`, typed `defineModel` defaults, user text during hydration, and setup state after asynchronous SSR. New helpers coexist with older ref patterns, leaving most application components untouched.
Docs5/5The official site separates the tutorial, guide, examples, API reference, style guidance, TypeScript, server rendering, performance, accessibility, and tooling. Many guide pages switch between Composition API and Options API examples, so support for both styles is explicit. Dedicated reactivity pages explain ref unwrapping and destructuring limits. The amount of choice can still overwhelm a new team, especially when core Vue, Vite, Router, Pinia, and Nuxt docs overlap.
Maintenance5/5npm published 3.5.41 on August 5, 2026, while GitHub showed a push on August 26, 54,251 stars, 914 open issues and pull requests, and an unarchived repository. The stable patch addresses compiler caching, custom elements, SSR, hydration, scheduling, slots, Teleport, transitions, `v-model`, and TypeScript inference. Vue 3.6 release candidates are also active, so teams should distinguish the stable 3.5 line from prerelease Vapor work.
Ecosystem5/5npm counted 15,332,402 Vue downloads in the week ending August 25, 2026. Vue Router and Pinia cover routing and state, Vite is the standard scaffold path, and Nuxt provides a full server-rendered application framework. Browser devtools and many component systems understand single-file components. Ecosystem fit still depends on project constraints because some commercial widgets, native stacks, and organization-wide design systems target React first or exclusively.

Use it if

  • The team prefers template directives and single-file components to JSX as the default component syntax.
  • A server-rendered site needs isolated interactive components or a full client application with the same component model.
  • Composition API and `<script setup>` fit the project's TypeScript and code-reuse conventions.
  • Vue Router, Pinia, Vite, or Nuxt cover the surrounding architecture without forcing many unrelated integration choices.
Skip it if

Setup reality

We installed Vue 3.5.41 in a fresh Node 22 Bookworm sandbox in 4.9 seconds. npm left 30 packages using 19 MB and found 0 known vulnerabilities. The package itself is 2,600 KB unpacked, lists 5 direct dependencies and 1 peer dependency, and bundles TypeScript declarations. It publishes CommonJS and ESM paths through an exports map; both require() and ESM import worked in our check.

A namespace import bundled by esbuild for the browser produced 122.5 KB minified and 47.8 KB gzipped. That is our full-package probe, not a promise for every Vue application. Production output changes with imported APIs, compiler mode, tree shaking, routes, and component libraries. Build the real entry and inspect its chunks. The package lists TypeScript as a peer, while JavaScript-only applications can use Vue without adopting TypeScript.

The official project scaffold asks separately about TypeScript, Vue Router, Pinia, unit tests, end-to-end tests, linting, and formatting because the core package does not settle them. In script code, a ref's value lives at .value; templates unwrap refs. Destructuring primitive properties from reactive() breaks their live connection unless toRef() or toRefs() is used. Choose Composition API with <script setup> or Options API as a team default before the component count grows.

Server rendering uses the included server-renderer entry or a framework such as Nuxt, but hydration still requires matching server and client trees. Version 3.5.41 fixes hidden-state normalization, async setup restoration, and preservation of text entered before hydration. Application code can still create mismatches with dates, random values, browser-only branches, or request-specific state. Give v-for rows stable domain keys, cancel stale watcher work, remove global listeners on unmount, and avoid deep watchers over large objects.

Patterns

Build a typed counter component create-script-setup-component

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

const count = ref(0)
</script>

<template>
  <button type="button" @click="count++">
    Count: {{ count }}
  </button>
</template>

Top-level script bindings are visible in the template, where the ref is automatically unwrapped. Script code still uses `count.value`.

Destructure state with toRefs preserve-reactive-properties

import { reactive, toRefs } from 'vue'

const state = reactive({
  customer: 'Asha',
  paid: false,
})

const { customer, paid } = toRefs(state)

Plain destructuring copies primitive values out of a reactive proxy. `toRefs()` keeps both properties connected to `state`.

Cache an invoice total derive-computed-total

import { computed, ref } from 'vue'

const lines = ref([{ price: 120 }, { price: 80 }])
const total = computed(() =>
  lines.value.reduce((sum, line) => sum + line.price, 0),
)

A computed getter should derive output without modifying `lines`. Vue caches it until a tracked dependency changes.

Abort an outdated search cancel-stale-watcher

import { ref, watch } from 'vue'

const query = ref('')
const results = ref([])

watch(query, async (value, _oldValue, onCleanup) => {
  const controller = new AbortController()
  onCleanup(() => controller.abort())
  results.value = await search(value, controller.signal)
})

Cleanup runs before the watcher starts again, preventing an older request from overwriting a newer result.

Declare a component boundary type-props-and-events

<script setup lang="ts">
const props = defineProps<{ title: string; count?: number }>()
const emit = defineEmits<{ save: [id: number] }>()

function save() {
  emit('save', 42)
}
</script>

`defineProps` and `defineEmits` are compiler macros. Importing them from `vue` is unnecessary.

Back a child input with v-model implement-component-model

<!-- CustomerName.vue -->
<script setup lang="ts">
const model = defineModel<string>({ required: true })
</script>

<template>
  <input v-model="model" autocomplete="name" />
</template>

`defineModel()` creates the model prop and update event. Version 3.5.41 fixes inference when factory defaults are involved.

Focus an input after mount focus-template-element

<script setup lang="ts">
import { onMounted, useTemplateRef } from 'vue'

const searchBox = useTemplateRef<HTMLInputElement>('search')
onMounted(() => searchBox.value?.focus())
</script>

<template><input ref="search" /></template>

The template ref is null before mount and becomes null again if the input is removed by a conditional branch.

Preserve row identity render-keyed-list

<template>
  <p v-if="items.length === 0">No results</p>
  <ul v-else>
    <li v-for="item in items" :key="item.id">
      {{ item.name }}
    </li>
  </ul>
</template>

Use a stable record ID when rows can move or contain local input state. An array index changes identity after insertion or sorting.

Track online status without leaking listeners extract-browser-composable

import { onBeforeUnmount, onMounted, ref } from 'vue'

export function useOnlineStatus() {
  const online = ref(navigator.onLine)
  const update = () => { online.value = navigator.onLine }

  onMounted(() => {
    window.addEventListener('online', update)
    window.addEventListener('offline', update)
  })
  onBeforeUnmount(() => {
    window.removeEventListener('online', update)
    window.removeEventListener('offline', update)
  })
  return online
}

Global listeners survive component removal unless the composable unregisters both of them during unmount.

Split an admin panel lazy-load-component

import { defineAsyncComponent } from 'vue'

const AdminPanel = defineAsyncComponent(() =>
  import('./AdminPanel.vue'),
)

A static import path lets the bundler create a predictable separate chunk. Add loading and error UI for slow or failed downloads.

Share a typed theme ref provide-typed-context

import { inject, provide, ref, type InjectionKey, type Ref } from 'vue'

const themeKey: InjectionKey<Ref<string>> = Symbol('theme')
provide(themeKey, ref('dark'))

const theme = inject(themeKey)
if (!theme) throw new Error('theme provider missing')

A symbol avoids string-key collisions and carries the injected type. Handle the missing-provider case explicitly.

Move dialog markup under body teleport-accessible-dialog

<template>
  <Teleport to="body">
    <div v-if="open" role="dialog" aria-modal="true">
      <button type="button" @click="open = false">Close</button>
    </div>
  </Teleport>
</template>

Teleport changes DOM placement while retaining component ownership. Focus trapping, Escape handling, and focus restoration still need implementation.

Alternatives

PackageRegistryPick it when
reactnpmUse it when required libraries, React Native, or the team's existing component investment decides the ecosystem.
sveltenpmUse it when compile-time component behavior and Svelte's state syntax fit the team better than Vue's runtime model.
solid-jsnpmUse it for JSX with fine-grained reactive updates and a smaller conceptual runtime surface.

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.