---
title: "2025 in review: what learners got stuck on · vuejs.school"
description: "Five Vue ideas the quiz questions keep coming back to, why each one is confusing, the one sentence that fixes it, and what changed around Vue in 2025."
canonical_url: "https://vuejs.school/community/blog/2025-in-review-what-learners-got-stuck-on"
meta:
  "og:description": "Five Vue ideas the quiz questions keep coming back to, why each one is confusing, the one sentence that fixes it, and what changed around Vue in 2025."
  "og:image": "https://vuejs.school/_og/d/a_~MjAyNSBpbiByZXZpZXc6IHdoYXQgbGVhcm5lcnMgZ290IHN0dWNrIG9uIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~MjAyNSBpbiByZXZpZXc6IHdoYXQgbGVhcm5lcnMgZ290IHN0dWNrIG9u%2Cdescription_~Rml2ZSBWdWUgaWRlYXMgdGhlIHF1aXogcXVlc3Rpb25zIGtlZXAgY29taW5nIGJhY2sgdG8sIHdoeSBlYWNoIG9uZSBpcyBjb25mdXNpbmcsIHRoZSBvbmUgc2VudGVuY2UgdGhhdCBmaXhlcyBpdCwgYW5kIHdoYXQgY2hhbmdlZCBhcm91bmQgVnVlIGluIDIwMjUu%2Ceyebrow_~QmVoaW5kIHRoZSBzY2VuZXMgwrcgNiBtaW4gcmVhZA%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy8yMDI1LWluLXJldmlldy13aGF0LWxlYXJuZXJzLWdvdC1zdHVjay1vbiI%2Cs_TjPHQdaYowmq1Scn.png"
  "og:image:alt": "2025 in review: what learners got stuck on · vuejs.school"
  "og:image:height": "600"
  "og:image:type": image/png
  "og:image:width": "1200"
  "og:title": "2025 in review: what learners got stuck on · vuejs.school"
  "twitter:image": "https://vuejs.school/_og/d/a_~MjAyNSBpbiByZXZpZXc6IHdoYXQgbGVhcm5lcnMgZ290IHN0dWNrIG9uIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~MjAyNSBpbiByZXZpZXc6IHdoYXQgbGVhcm5lcnMgZ290IHN0dWNrIG9u%2Cdescription_~Rml2ZSBWdWUgaWRlYXMgdGhlIHF1aXogcXVlc3Rpb25zIGtlZXAgY29taW5nIGJhY2sgdG8sIHdoeSBlYWNoIG9uZSBpcyBjb25mdXNpbmcsIHRoZSBvbmUgc2VudGVuY2UgdGhhdCBmaXhlcyBpdCwgYW5kIHdoYXQgY2hhbmdlZCBhcm91bmQgVnVlIGluIDIwMjUu%2Ceyebrow_~QmVoaW5kIHRoZSBzY2VuZXMgwrcgNiBtaW4gcmVhZA%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy8yMDI1LWluLXJldmlldy13aGF0LWxlYXJuZXJzLWdvdC1zdHVjay1vbiI%2Cs_TjPHQdaYowmq1Scn.png"
  "twitter:image:alt": "2025 in review: what learners got stuck on · vuejs.school"
---

> Index of every page on this site: [llms.txt](https://vuejs.school/llms.txt)

## Losing reactivity with `.value` and destructuring

The first theme is the oldest. A `ref` is a box: an object whose `.value` property holds the value. Vue opens the box for you in templates, so `{{ count }}` works. Your script gets no such help: `if (count > 3)` compares an object with a number, and `const n = count.value` copies a number that will never update. The rule is simple to state and easy to forget under pressure, because half the time Vue hides the box and half the time it does not.

Destructuring is the same confusion in a different shape. Pulling a property off a `reactive()` object gives you the plain value at that moment:

```
const state = reactive({ count: 0 })
const { count } = state // a number, frozen at 0
```

```
const { count } = toRefs(state) // a ref that follows state.count
```

What made this harder in 2025 is that destructuring props now does work, because the Vue 3.5 compiler rewrites `const { title } = defineProps<...>()` into a reactive access. So props yes, `reactive()` no, and the two are easy to mix up when both sit in one component. The sentence to keep: a ref is a box, the template opens it for you and your script does not. The January post on `ref()` and `reactive()` and the February deep dive on props destructure cover the rest.

## `storeToRefs` skips your actions

The second theme is one specific line. Pinia's `storeToRefs()` turns a store's state and getters into refs, so that you can destructure them without losing reactivity. It skips actions on purpose: an action is a plain function, and wrapping it in a ref would make no sense. With `cart` taken from `useCartStore()`, the result is a line that looks right and fails at the first call:

```
const { items, total, add } = storeToRefs(cart)
add('Sticker') // TypeError: add is not a function
```

```
const { items, total } = storeToRefs(cart)
const { add } = cart
```

Actions can be destructured straight from the store, because Pinia binds them to it. The sentence: `storeToRefs` for state and getters, the store itself for actions. The May deep dive on how a Pinia store is put together explains why getters become refs and actions do not, and the August post on stores that use other stores shows the pattern inside a store.

## Choosing between `computed` and `watch`

The third theme is a choice rather than a trap. Both `computed` and `watch` react to changes, so when a value depends on two other values, a lot of people reach for a watcher that assigns the result to a ref:

```
const total = ref(0)
watch([price, qty], () => { total.value = price.value * qty.value })
```

It works, mostly. But a watcher runs only when its sources change, so until `price` or `qty` changes, the page shows the starting `0`. And two places now hold the same truth. A computed value has neither problem:

```
const total = computed(() => price.value * qty.value)
```

The sentence: `computed` for a value you can calculate, `watch` for a thing you must do, such as saving, logging or talking to the network. The February decision guide gives the long form. The April post on watchers covers the follow-up mistakes, among them passing `state.count` to `watch` instead of a getter, which hands it a number and watches nothing.

## State that leaks between visitors on the server

The fourth theme only appears once an app renders on the server, which is why it surprises people who learned Vue in a single-page app. A `ref` declared at the top of a composable file is created once, when the module loads. In the browser that module belongs to one person. On a Nuxt server it belongs to the process, and every request shares it, so a visitor can be shown the previous visitor's user object.

*app/composables/useUser.ts**Breaks***

```
const user = ref<User | null>(null) // module scope: one for the whole server

export function useUser() {
  return user
}
```

*app/composables/useUser.ts**Works***

```
export function useUser() {
  return useState<User | null>('user', () => null)
}
```

`useState` stores the value on the Nuxt app instance, which is created per request, and sends it to the client in the payload so hydration matches. A Pinia store does the same through the Nuxt module. The sentence: on the server a module-level ref is shared by every visitor; per-request state lives in `useState` or in a store. The December post on `useState`, `ref` and Pinia in a Nuxt app walks through the decision.

## When `useFetch` runs, and when it runs again

The fifth theme is timing. People expect `useFetch` to behave like a `fetch` call placed where it is written, running every time the component renders or every time a variable it mentions changes. It does neither.

It runs once, during setup. For a page rendered on the server, that happens on the server, and the result travels to the client in the payload so the browser does not fetch again. After that it runs again only when something it watches changes, or when you call `refresh()`. A URL built from a plain string is fixed at setup:

```
const page = ref(1)
const { data } = await useFetch(`/api/posts?page=${page.value}`) // the string never changes
```

```
const page = ref(1)
const { data } = await useFetch('/api/posts', { query: { page } }) // a ref: refetches when page changes
```

The sentence: `useFetch` runs once in setup, and again only when something it watches changes or you call `refresh()`. The April post on the reworked async data layer in Nuxt 3.17, the July post on the data fetching changes in Nuxt 4, and the October post on abort signals in Nuxt 4.2 each add a layer; read them in that order.

## What changed around Vue in 2025

Vue itself stayed on 3.5 all year, and that stability is why the themes above are about understanding rather than migration. Around it, a lot moved:

- **Pinia 3** arrived in February and dropped Vue 2.
- **Vite 7** landed in June, with a higher Node requirement (20.19 or 22.12) and Baseline browser targets.
- **Nuxt 4** shipped on 15 July with the `app/` directory and the data fetching changes, followed by 4.1 in September and 4.2 in October.
- **Vitest 4** made Browser Mode stable on 22 October, the same day **VueUse 14** set Vue 3.5 as its minimum.
- **Vite 8**, with Rolldown as its bundler, has been in beta since 3 December.
- **Vue 3.6.0-beta.1** was published on 23 December. Vapor Mode and the new reactivity system are now in a beta, not an alpha, which means the APIs are settling. It is still not something to put in production.

Most of those releases have a release-notes post from this year that says what the change asks of an existing app.

## What the school adds in 2026

The shape of a lesson is not changing: a short tutorial with a runnable example, then a quiz drawn from the reviewed question bank, with reviews scheduled by spaced repetition so that what you got wrong comes back before you forget it again.

The optional AI instructor keeps its limits too. It can choose which questions to show you and give hints, and it never writes questions. It stays off until you switch it on. You can run it with your own API key for Anthropic, OpenAI, Google Gemini or any OpenAI-compatible server, and then requests go from your browser straight to the provider you chose. With it off, nothing you type in a lesson leaves your browser.

What grows in 2026 is the amount of practice: more lessons, and more questions for the lessons that exist, especially around the five themes above. There, a second and third question from a different angle is what turns a right answer by elimination into a right answer by understanding. The blog continues at two posts a week: release notes when something ships, tutorials and deep dives in between.

**Was this helpful?**

## **Keep reading**

[Learning tips · 6 min read**Why your quiz brings old questions back**](https://vuejs.school/community/blog/why-your-quiz-brings-old-questions-back) [Learning tips · 7 min read**Finished a track? Here is how to keep the knowledge**](https://vuejs.school/community/blog/finished-a-track-how-to-keep-the-knowledge)

```json
{"@context":"https://schema.org","@graph":[{"@id":"https://vuejs.school/#website","@type":"WebSite","description":"A free AI-instructed school for Vue.js. Short tutorials, runnable code and quizzes that adapt to you.","name":"vuejs.school","url":"https://vuejs.school/","publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/2025-in-review-what-learners-got-stuck-on#webpage","@type":"WebPage","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2025-12-30","description":"Five Vue ideas the quiz questions keep coming back to, why each one is confusing, the one sentence that fixes it, and what changed around Vue in 2025.","name":"2025 in review: what learners got stuck on","url":"https://vuejs.school/community/blog/2025-in-review-what-learners-got-stuck-on","breadcrumb":{"@id":"https://vuejs.school/community/blog/2025-in-review-what-learners-got-stuck-on#breadcrumb"},"isPartOf":{"@id":"https://vuejs.school/#website"},"potentialAction":[{"@type":"ReadAction","target":["https://vuejs.school/community/blog/2025-in-review-what-learners-got-stuck-on"]}],"primaryImageOfPage":{"@id":"https://vuejs.school/#logo"}},{"@id":"https://vuejs.school/#identity","@type":"Organization","description":"A free AI-instructed school for Vue.js. Short tutorials, runnable code and quizzes that adapt to you.","name":"vuejs.school","url":"https://vuejs.school","logo":{"@id":"https://vuejs.school/#logo"}},{"@id":"https://vuejs.school/community/blog/2025-in-review-what-learners-got-stuck-on#article","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2025-12-30","description":"Five Vue ideas the quiz questions keep coming back to, why each one is confusing, the one sentence that fixes it, and what changed around Vue in 2025.","headline":"2025 in review: what learners got stuck on","inLanguage":"en","isAccessibleForFree":true,"thumbnailUrl":"https://vuejs.school/_og/d/a_~MjAyNSBpbiByZXZpZXc6IHdoYXQgbGVhcm5lcnMgZ290IHN0dWNrIG9uIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~MjAyNSBpbiByZXZpZXc6IHdoYXQgbGVhcm5lcnMgZ290IHN0dWNrIG9u%2Cdescription_~Rml2ZSBWdWUgaWRlYXMgdGhlIHF1aXogcXVlc3Rpb25zIGtlZXAgY29taW5nIGJhY2sgdG8sIHdoeSBlYWNoIG9uZSBpcyBjb25mdXNpbmcsIHRoZSBvbmUgc2VudGVuY2UgdGhhdCBmaXhlcyBpdCwgYW5kIHdoYXQgY2hhbmdlZCBhcm91bmQgVnVlIGluIDIwMjUu%2Ceyebrow_~QmVoaW5kIHRoZSBzY2VuZXMgwrcgNiBtaW4gcmVhZA%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy8yMDI1LWluLXJldmlldy13aGF0LWxlYXJuZXJzLWdvdC1zdHVjay1vbiI%2Cs_TjPHQdaYowmq1Scn.png","@type":["Article","BlogPosting"],"articleSection":["Behind the scenes"],"author":{"@id":"https://vuejs.school/#identity"},"image":{"@id":"https://vuejs.school/#/schema/image/1"},"isPartOf":{"@id":"https://vuejs.school/community/blog/2025-in-review-what-learners-got-stuck-on#webpage"},"mainEntityOfPage":{"@id":"https://vuejs.school/community/blog/2025-in-review-what-learners-got-stuck-on#webpage"},"publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/2025-in-review-what-learners-got-stuck-on#breadcrumb","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","name":"Blog","item":"https://vuejs.school/community/blog","position":1},{"@type":"ListItem","name":"2025 in review: what learners got stuck on","position":2}]},{"@id":"https://vuejs.school/#logo","@type":"ImageObject","caption":"vuejs.school","contentUrl":"https://vuejs.school/apple-touch-icon.png","inLanguage":"en","url":"https://vuejs.school/apple-touch-icon.png"},{"@id":"https://vuejs.school/#/schema/image/1","@type":"ImageObject","contentUrl":"https://vuejs.school/_og/d/a_~MjAyNSBpbiByZXZpZXc6IHdoYXQgbGVhcm5lcnMgZ290IHN0dWNrIG9uIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~MjAyNSBpbiByZXZpZXc6IHdoYXQgbGVhcm5lcnMgZ290IHN0dWNrIG9u%2Cdescription_~Rml2ZSBWdWUgaWRlYXMgdGhlIHF1aXogcXVlc3Rpb25zIGtlZXAgY29taW5nIGJhY2sgdG8sIHdoeSBlYWNoIG9uZSBpcyBjb25mdXNpbmcsIHRoZSBvbmUgc2VudGVuY2UgdGhhdCBmaXhlcyBpdCwgYW5kIHdoYXQgY2hhbmdlZCBhcm91bmQgVnVlIGluIDIwMjUu%2Ceyebrow_~QmVoaW5kIHRoZSBzY2VuZXMgwrcgNiBtaW4gcmVhZA%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy8yMDI1LWluLXJldmlldy13aGF0LWxlYXJuZXJzLWdvdC1zdHVjay1vbiI%2Cs_TjPHQdaYowmq1Scn.png","inLanguage":"en","url":"https://vuejs.school/_og/d/a_~MjAyNSBpbiByZXZpZXc6IHdoYXQgbGVhcm5lcnMgZ290IHN0dWNrIG9uIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~MjAyNSBpbiByZXZpZXc6IHdoYXQgbGVhcm5lcnMgZ290IHN0dWNrIG9u%2Cdescription_~Rml2ZSBWdWUgaWRlYXMgdGhlIHF1aXogcXVlc3Rpb25zIGtlZXAgY29taW5nIGJhY2sgdG8sIHdoeSBlYWNoIG9uZSBpcyBjb25mdXNpbmcsIHRoZSBvbmUgc2VudGVuY2UgdGhhdCBmaXhlcyBpdCwgYW5kIHdoYXQgY2hhbmdlZCBhcm91bmQgVnVlIGluIDIwMjUu%2Ceyebrow_~QmVoaW5kIHRoZSBzY2VuZXMgwrcgNiBtaW4gcmVhZA%2Ctint_sand%2Cp_Ii9jb21tdW5pdHkvYmxvZy8yMDI1LWluLXJldmlldy13aGF0LWxlYXJuZXJzLWdvdC1zdHVjay1vbiI%2Cs_TjPHQdaYowmq1Scn.png"}]}
```