skip to content

Mixins & Extends

Mixins and extends merge options into a component under per-option strategies, hiding where a property came from and colliding on names. Interviewers ask why composables replaced them.

part ofVue.jsoverview, primer and where to startread it →
on this pageshow

explore

questions

4

Why does the Vue 3 documentation recommend composables instead of mixins for sharing logic between components?

level: juniorimportance: must knowfreq 60%

answer

  1. where did this property come from
  2. same key, two mixins
  3. mixins talk through this
  4. rename on destructure, pass as argument

basics

~20 s

The Vue 3 docs name three mixin drawbacks: no way to tell which mixin added a property, silent collisions on shared keys, and coupling through shared keys. Composables return explicit values you name, rename and pass on.

solid answer

~40 s

The Vue 3 docs list three drawbacks. First, **unclear source of properties**: with several mixins, nothing in the component says which one put `loading` or `fetchPage` on `this`. Second, **namespace collisions**: two mixins can register the same key and one silently wins. Third, **implicit cross-mixin communication**: mixins that need each other must agree on property names on `this`, so they are coupled invisibly. A composable is a function that returns refs and functions; you destructure what you need, so the source is visible at the call site, you can rename on conflict (`const { page: orderPage } = usePagination()`), and one composable's output is passed to another as a plain argument. Mixins stay supported, mainly for migration and ecosystem compatibility.

code

ts · 11 lines
ts
// usePagination.ts
import { ref, computed } from 'vue'

export function usePagination(pageSize: number) {
  const page = ref(1)
  const offset = computed(() => (page.value - 1) * pageSize)
  function next() {
    page.value++
  }
  return { page, offset, next }
}

go deeper

for a junior

Recall the three drawbacks the Vue docs name — unclear property source, name collisions, implicit coupling between mixins — and that mixins still work in Vue 3.

for a middle

Map each drawback to the composable feature that removes it: an explicit return value, renaming while destructuring, and passing values as arguments.

for a senior

Show you can judge when a mixin in an existing codebase is worth converting, and why a global mixin is the first candidate for removal.

for a principal

Treat the shift as a codebase policy: explicit dependencies make ownership reviewable, and a rule of no new mixins keeps legacy ones from spreading while old ones are retired.

## What each mechanism is A **mixin** is an options object — `data`, `methods`, `computed`, hooks — that Vue merges into a component listed with `mixins: [...]`. Everything it declares lands on the component instance, `this`, alongside the component's own properties. A **composable** is an ordinary function, conventionally named `useSomething`, that uses Vue's Composition API (`ref`, `computed`, `watch`, lifecycle registration functions) and **returns** the state and functions it creates. The component calls it and keeps the returned values in local variables. Both let several components reuse the same stateful logic. The difference is how the reused pieces reach the component: by being merged invisibly into `this`, or by being handed back as values. ## The three drawbacks the Vue docs name The Vue 3 guide on composables states three problems with mixins and, for these reasons, no longer recommends them. 1. **Unclear source of properties.** When a component uses three mixins and its template reads `total`, nothing in the file tells you which mixin defined it. You open each mixin, and its own nested `mixins`, until you find it. Tooling cannot help much either, because the property only exists after a runtime merge. 2. **Namespace collisions.** Mixins written by different authors can declare the same key. The merge rules resolve it — for `data`, `methods` and `computed`, the later source wins — and they do so silently. One mixin's `reset()` quietly replaces another's. 3. **Implicit cross-mixin communication.** If mixin B needs a value that mixin A owns, B reads `this.someKey` and simply hopes A is present and still uses that name. The dependency is real but written nowhere. ## How composables answer each one | Problem | Mixin | Composable | |---|---|---| | Where a value comes from | Merged into `this`; source invisible | Returned by a named call; visible at the call site | | Two sources, same name | Later mixin wins silently | Rename while destructuring | | One piece needs another's value | Shared key on `this` | Pass the value as a function argument | | Two independent copies in one component | Impossible — one mixin, one set of keys | Call the function twice | The last row is worth saying out loud in an interview. A mixin can be listed only once in a meaningful way, so a component cannot hold two independent paginators from the same mixin. A composable is just a function: calling it twice gives two separate sets of refs. The Vue docs also recommend composables return a plain object of refs precisely so the consumer can **destructure** it — that is what makes the source of each variable readable in the component. ## A worked contrast Consider a `paginationMixin` that declares `page` in `data()` and `nextPage()` in `methods`. A component using it reads `this.page` in a template, with no hint of where `page` lives. If the same component later adds a `filterMixin` that also declares `page` for its own purposes, the two silently share one property. And if the component needs to paginate both orders and reviews, the mixin offers no way to have two pages. The composable version, `usePagination(pageSize)`, returns `{ page, offset, next }`. The component writes `const { page: orderPage } = usePagination(20)` and `const { page: reviewPage } = usePagination(5)` — two independent states, each named where it is created, and the page size passed as an argument rather than read from an agreed key. ## What has not changed - Mixins **still work** in Vue 3: the `mixins` option, `extends` and `app.mixin()` are all supported. - The docs describe them as kept for migration, familiarity and ecosystem compatibility, and advise avoiding them — especially global mixins registered with `app.mixin()` — in application code. - Composables are not a separate runtime feature; they use the same reactivity system options components use, which is why a legacy component can adopt them gradually. ## Common weak answers - "Mixins are slower" — performance is not among the reasons the docs give; the problems are about **readability and coupling**. - "Mixins were removed in Vue 3" — they were not. - "Composables avoid collisions automatically" — they do not; you still resolve a clash, but you do it yourself, **explicitly**, by renaming. ## How to answer in an interview Name the three drawbacks in the docs' words, then map each to the composable feature that removes it: an explicit return value, renaming at destructuring, and function arguments instead of shared keys. Close by noting that the change is about making dependencies explicit, not about adding capability.

  • Why is a global mixin registered with app.mixin() considered worse than a local one?
    `app.mixin()` merges its options into every component in the application, including third-party ones. Its hooks run in every instance, and its keys can collide with properties in components whose authors never saw it. The unclear-source problem becomes app-wide, which is why the Vue docs single global mixins out as something to avoid in application code.
  • If composables are preferred, can an existing options component use one without being rewritten?
    Yes. An options component can declare a `setup()` function, call the composable there and return the values it needs, which then appear on `this` and in the template beside `data` and `methods`. That makes it possible to replace one mixin at a time instead of rewriting the component.

Mixins are like several colleagues writing on one shared whiteboard without initials: you cannot tell who wrote a line, and two people can overwrite the same spot. A composable is a colleague handing you a labelled envelope — you know who sent it and you file it under whatever name you like.

saying these in an interview costs you the question

  • Mixins were removed in Vue 3, so composables are the only option.
  • Composables are preferred mainly because mixins make rendering slower.
  • Composables prevent name collisions automatically without any renaming.
  • A mixin can be applied twice to hold two independent copies of its state.
  • Composables need a different reactivity system from options components.
open as a page

In Vue 3's Options API, when a mixin and its component both define data, methods, watch and created, how is each merged?

level: middleimportance: should knowfreq 48%

basics

~20 s

Vue 3 merges each option by its own strategy: data() results are shallow-merged, methods are object-merged, and created hooks and watch handlers are queued so all run, mixins first. On a name clash the component's own value wins.

open as a page

A Vue 3 options component uses two mixins that each declare a loading flag, and its spinner vanishes before the data arrives; how do you diagnose and fix it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Both mixins' data() return loading, so Vue 3 merges them into one reactive key with no warning; whichever request finishes first sets it false. Fix by giving each concern its own state, ideally a composable per mixin combined with a computed.

open as a page

In Vue 3's Options API, how does the extends option differ from mixins, and what does an extending component not inherit?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

Vue 3's extends takes one base component and merges it exactly like a first mixin; the difference is intent, inheritance versus composing features. It does not merge setup(), ignores expose, and the component's render function is not inherited.

open as a page