vue-demi: the shim that redirects one import and eats its own errors
GitHub describes it as 🎩 Creates Universal Library for Vue 2 & 3. The repository metadata lists JavaScript as its primary language. The metadata lists the MIT license. This article stays within the project description and details documented in the GitHub repository README.
At a glance
- What is it?
- A utility that lets a library support Vue 2 and Vue 3 through a single import path, implemented as an install-time redirect with a small CLI to repair or re-point it. The README tells you not to start a new project on it, and the release history agrees.
- Who is it for?
- vue-demi is still the right answer for a library that must support Vue 2.6 through Vue 3 without duplicating its source, and the peer dependency setup plus one Vite line are a small price. Two checks first.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Probably not. The repository last received commits 22 months ago, on January 22, 2025.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- vueuse/vue-demi3,124 ★ · -6
| Repository | 2026-08-13 | 2026-11-14 |
|---|---|---|
| vueuse/vue-demi | 3.1k | 3.1k |
-6 Stars in the last 93 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The README opens with a caution, not a pitch
The first block in the documentation is a callout, and it is negative. It says vue-demi should not be used for new projects and will be deprecated in the future. The dates line up with that. The most recent release is v0.14.10 from 2024-07-25, preceded by v0.14.9 two days earlier and v0.14.8 in May 2024, so the version has not moved in over two years, while the last push to the main branch is dated 2025-01-22 and the repository is not archived. The package identity is also split across two places worth knowing about. The code sits in the vueuse organisation, while the repository field in package.json points at antfu's own repository and the funding and author fields point at Anthony Fu, who wrote the linked post explaining the approach. The name is the French word for half, which is the whole idea in one word.
Three strategies behind one import path
The redirect has exactly three strategies, and the boundary between them is a Vue version number.
import { ref, reactive, defineComponent } from 'vue-demi'For 2.6 and below, the exports come from vue plus @vue/composition-api, with the plugin installed automatically. For 2.7 they come from vue alone, because the Composition API is built into that release. For 3.0 and above they come from vue with a polyfill for Vue 2's set and del API, so code written against those two methods keeps compiling. Nothing in your source changes between those cases: you import from vue-demi and the installed version decides where the symbols come from. The consequence to keep in mind is that the decision is made once, at install time, which is why the CLI exists and why changing your Vue version later can leave the redirect pointing at the wrong place.
Peer dependencies are how you declare support
Installation is an ordinary dependency, and the version claim is expressed in peer dependencies rather than in code.
{
"dependencies": {
"vue-demi": "latest"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^2.0.0 || >=3.0.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
},
"devDependencies": {
"vue": "^3.0.0"
}
}The composition API package is marked optional, which is the part that makes a single library usable from Vue 3 as well, since a Vue 3 consumer will never install it. The peer range spans from Vue 2 through the current 3.x line, and your own devDependency picks the version you develop against, with a comment suggesting 2.6 instead if that is your working environment. Note that the shipped manifest narrows the range slightly, using a caret on Vue 3 plus a prerelease tag and an explicit 2.6 floor, so the range you declare and the range the package declares are not identical.
The postinstall hook discards every error it meets
This is the single most important line in the package for anyone debugging an install.
"postinstall": "node -e \"try{require('./scripts/postinstall.js')}catch(e){}\"The hook runs the script that writes the redirect, wrapped in a try and catch whose body is empty, so a failure produces no warning, no non-zero exit, and no record of what went wrong. That is deliberate, since a library dependency should never fail an install that is otherwise fine, but it means a broken redirect is silent. Two situations are named as the reasons to intervene: the postinstall hook not being triggered at all, which happens in some install setups, and having updated your Vue version, which leaves the previous redirect describing a Vue that is no longer installed.
npx vue-demi-fixThat command is the repair path, and it is why it ships as a binary alongside the switch command.
vue-demi-switch can also rename what vue resolves to
The switch command takes a version, and optionally an alias.
npx vue-demi-switch 2
# or
npx vue-demi-switch 3With an alias, the redirect stops pointing at vue and points at a name you choose, which is how the isomorphic testing recipe works. Declare both majors in devDependencies under npm aliases, one as the real vue and one as an alias, then switch before each test run so the same test suite executes against both.
{
"scripts": {
"test:2": "vue-demi-switch 2 vue2 && jest",
"test:3": "vue-demi-switch 3 && jest",
},
"devDependencies": {
"vue": "^3.0.0",
"vue2": "npm:vue@2"
}
}The generated module for the alias case re-exports everything from the alias and additionally exports the environment flags, so a test can assert which version it ran against instead of guessing.
Three extra APIs for the awkward cases
Beyond re-exporting Vue, the package exposes three things that exist to solve specific problems. The first pair is isVue2 and isVue3, boolean flags for branching when you genuinely need version-specific logic rather than a polyfilled call. The second is the Vue2 export, which gives access to Vue 2's global API without dragging in every tree-shakable module; the linked issue explains the request, and the documented use is pushing entries onto Vue2.config.ignoredElements. The third is install(). In Vue 2 the Composition API is a plugin that has to be installed on the instance before use, and vue-demi attempts that automatically, so install() is exposed as a safe version of Vue.use(CompositionAPI) for the cases where the automatic path is not enough. Under Vue 3 it is an empty function, so calling it unconditionally is safe.
Three builds, one Vite line, and a Node floor of 12
The package manifest describes the distribution precisely. The CommonJS entry is lib/index.cjs, the ES module entry is lib/index.mjs, and the browser CDN file is lib/index.iife.js, which is what both jsdelivr and unpkg are pointed at. Types come from lib/index.d.ts, and the exports map exposes the root plus a wildcard that passes any subpath through unchanged. Only three directories are published: lib, bin, and scripts, which is consistent with the CLI binaries being installed alongside the runtime. The engines field asks for Node 12 or newer, and the only two npm scripts are the postinstall hook and a release script that bumps the version with a tag, commit, and push before publishing.
The one framework-specific requirement is Vite, where pre-bundling has to be turned off for this package.
// vite.config.js
export default defineConfig({
optimizeDeps: {
exclude: ['vue-demi']
}
})Without that exclusion the pre-bundled copy resolves before the install-time redirect has a say, which is the failure this line exists to prevent.
Editorial conclusion
vue-demi is still the right answer for a library that must support Vue 2.6 through Vue 3 without duplicating its source, and the peer dependency setup plus one Vite line are a small price. Two checks first. Read the caution at the top of the README, which says not to use it for new projects and that deprecation is planned, and confirm your build actually ran the postinstall step, since that hook is wrapped in a catch that discards every error. If either fails, npx vue-demi-fix is the repair.
Frequently asked questions
What is vue-demi?
A utility for writing universal Vue libraries that work on Vue 2 and Vue 3 from one source. You import from vue-demi instead of vue, and it redirects to Vue 2 with the Composition API, to Vue 2.7, or to Vue 3 depending on the user's environment.
Which Vue versions does vue-demi handle?
Three strategies: 2.6 and below re-export from vue plus @vue/composition-api with the plugin installed automatically, 2.7 re-exports from vue because the Composition API is built in, and 3.0 and above re-export from vue with a polyfill for Vue 2's set and del API.
Should I use vue-demi in a new project?
The README carries a caution that it should not be used for new projects and will be deprecated in the future. The most recent release is v0.14.10 from 2024-07-25, and the last push to the main branch is dated 2025-01-22.
Why does vue-demi need excluding from Vite pre-bundling?
Because Vite pre-bundles dependencies before the install-time redirect takes effect. The documented fix is adding vue-demi to optimizeDeps.exclude in vite.config.js. A related failure mode is a broken redirect, which you repair with npx vue-demi-fix.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vueuse-vue-demi)Star history chart for your README
[](https://hysenlabs.com/projects/vueuse-vue-demi)Was this useful?