feat(compat): add Vue 3 support via @vue/compat, round 2 (fixes #5196)

:question: What is it?
Version of Bootstrap-Vue which works for Vue 2 and Vue 3 (using @vue/compat)
Helps people with #5196
Demo using Vue 2 Demo using Vue 3
Additionally, it passes all (well, almost all, tiny fraction of tests are disabled because they are irrelevant for vue 3) tests in the test suite (it would be literally impossible to do this migration without a test suite)
This package uses vue 3 by default. If you want to run tests, build, etc. using vue 2 pass USE_VUE2=1 environment variable
:exclamation: How is this possible?
The heart of this PR consists of two parts:
- new vue-test-utils-compat package which allows us to run old test suite using new Vue (it's like
@vue/compatbut for tests - Additional compatibility layer for Vue3 crafted specifically for
bootstrap-vue
:wrench: How could I run it?
If you want just to play around - you can clone https://github.com/xanf/bootstrap-vue3-demo which has all required setup. If you want to try it on your own project, there is some setup required.
I'm skipping setup of @vue/compat, it is described in migration guide
You will need to monkey patch your Vue 3 a bit somewhere early in your app:
const originalVModelDynamicCreated = Vue.vModelDynamic.created;
const originalVModelDynamicBeforeUpdate = Vue.vModelDynamic.beforeUpdate;
Vue.vModelDynamic.created = function (el, binding, vnode) {
originalVModelDynamicCreated.call(this, el, binding, vnode);
if (!el._assign) {
el._assign = () => {};
}
};
Vue.vModelDynamic.beforeUpdate = function (el, binding, vnode) {
originalVModelDynamicBeforeUpdate.call(this, el, binding, vnode);
if (!el._assign) {
el._assign = () => {};
}
};
See https://github.com/vuejs/vue-next/pull/4121 for details
If your intention is to run your app in { MODE: 2 } (default @vue/compat) you're done.
If you want to have { MODE: 3 } (so all compats are disabled by default), additional setup is needed:
- if https://github.com/vuejs/vue-next/pull/4974 is not yet merged - you need to build
@vue/compatbuild with this fix included. https://github.com/xanf/bootstrap-vue3-demo already has patch insidepatchesfolder for that, installed by patch-package - Certain compat flags required to be enabled globally ATM and can't be disabled in this release (which maintains Vue 2 and Vue 3 compatibility):
-
GLOBAL_EXTEND,GLOBAL_MOUNT- for usingnew Vueinsidebootstrap-vue -
COMPONENT_FUNCTIONAL,RENDER_FUNCTION -
CUSTOM_DIR(anywhere where you use bootstrap-vue directive)
-
If you use portal-vue (which is still used for tooltips, etc.) you will need:
-
GLOBAL_SET
If you use old (for Vue 2) version of vue-router you will need:
-
CONFIG_OPTION_MERGE_STRATS -
GLOBAL_PRIVATE_UTIL -
GLOBAL_PROTOTYPE -
INSTANCE_EVENT_HOOKS -
OPTIONS_DESTROYED -
INSTANCE_EVENT_EMITTER
:bomb: What might not work
- Docs. I've tried to make
Nuxtrun using newer Nuxt 3, bridge, etc. but it was very problematic. So I wrote a script, which extracted demos from docs and generated https://github.com/xanf/bootstrap-vue3-demo with all demos - Build. It might or might not work, I didn't have an opportunity to test it yet
:arrow_upper_right: What's next?
Let's treat this one as "bridge" version Based on this branch I will create another one, which will be focused solely on full vue 3 compatibility (without using @vue/compat). While this will definitely take time, right now I do not see any major obstacles in gradual migration
:hugs: That's cool, how could I say "thank you"?
You're welcome, it's all about opensource. However, there are certain things, where your help will be appreciated:
- Help me spread the word about vue-test-utils-compat. You can retweet me or just drop a link to your friend who could be interested
- Check deployed versions of https://github.com/xanf/bootstrap-vue3-demo (links inside) and report about any issues you find out in Vue 3 version (it is very time consuming to test all possible scenarios)
PR checklist
What kind of change does this PR introduce? (check at least one)
- [ ] Bugfix (fixes a boo-boo in the code) -
fix(...), requires a patch version update - [x] Feature (adds a new feature to BootstrapVue) -
feat(...), requires a minor version update - [ ] Enhancement (augments an existing feature) -
feat(...), requires a minor version update - [ ] ARIA accessibility (fixes or improves ARIA accessibility) -
fix(...), requires a patch or minor version update - [ ] Documentation update (improves documentation or typo fixes) -
chore(docs), requires a patch version update - [x] Other: major update
Does this PR introduce a breaking change? (check one)
- [x] No (I know it's hard to believe)
- [ ] Yes (please describe since breaking changes require a minor version update)
The PR fulfills these requirements:
- [x] It's submitted to the
devbranch, not themasterbranch - [x] When resolving a specific issue, it's referenced in the PR's title (i.e.
[...] (fixes #xxx[,#xxx]), where "xxx" is the issue number) - [x] It should address only one issue or feature. If adding multiple features or fixing a bug and adding a new feature, break them into separate PRs if at all possible.
- [x] The title should follow the Conventional Commits naming convention (i.e.
fix(alert): not alerting during SSR render,docs(badge): update pill examples,chore(docs): fix typo in README, etc.). This is very important, as theCHANGELOGis generated from these messages, and determines the next version type (patch or minor).
If new features/enhancement/fixes are added or changed:
- [ ] Includes documentation updates
- [ ] Includes component package.json meta section updates (prop, slot and event changes/updates)
- [ ] Includes any needed TypeScript declaration file updates
- [ ] New/updated tests are included and passing (required for new features and enhancements)
- [x] Existing test suites are passing
- [x] CodeCov for patch has met target (all changes/updates have been tested)
- [x] The changes have not impacted the functionality of other components or directives
- [ ] ARIA Accessibility has been taken into consideration (Does it affect screen reader users or keyboard only users? Clickable items should be in the tab index, etc.)
If adding a new feature, or changing the functionality of an existing feature, the PR's description above includes:
- [x] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)
This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.
🔍 Inspect: https://vercel.com/bootstrap-vue/bootstrap-vue/AP9rnnFFga1HMqX9GxTngzbShiMk
✅ Preview: https://bootstrap-vue-git-vue3-compat-build-bootstrap-vue.vercel.app
This pull request is automatically built and testable in CodeSandbox.
To see build info of the built libraries, click here or the icon next to each commit SHA.
Latest deployment of this branch, based on commit 6ef2ba4cc161125a703c8953975bd68020ab3807:
| Sandbox | Source |
|---|---|
| BootstrapVue Starter Project | Configuration |
| BootstrapVue Nuxt.js Starter Project | Configuration |
Codecov Report
Merging #6905 (6ef2ba4) into dev (debe660) will not change coverage. The diff coverage is
100.00%.
@@ Coverage Diff @@
## dev #6905 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 299 305 +6
Lines 10265 10452 +187
Branches 2527 2550 +23
==========================================
+ Hits 10265 10452 +187
| Flag | Coverage Δ | |
|---|---|---|
| unittests | 100.00% <100.00%> (ø) |
Flags with carried forward coverage won't be shown. Click here to find out more.
| Impacted Files | Coverage Δ | |
|---|---|---|
| src/components/form-group/form-group.js | 100.00% <ø> (ø) |
|
| src/components/layout/col.js | 100.00% <ø> (ø) |
|
| src/components/layout/row.js | 100.00% <ø> (ø) |
|
| src/directives/scrollspy/scrollspy.js | 100.00% <ø> (ø) |
|
| src/components/alert/alert.js | 100.00% <100.00%> (ø) |
|
| src/components/aspect/aspect.js | 100.00% <100.00%> (ø) |
|
| src/components/avatar/avatar-group.js | 100.00% <100.00%> (ø) |
|
| src/components/avatar/avatar.js | 100.00% <100.00%> (ø) |
|
| src/components/badge/badge.js | 100.00% <100.00%> (ø) |
|
| src/components/breadcrumb/breadcrumb-item.js | 100.00% <100.00%> (ø) |
|
| ... and 180 more |
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.
What does this need to be merged? Looking forward to restart using bootstrap-vue with vue 3!
If you visit b-popover-advanced demo and click on button then we get below error:

I am also getting the same error in a few other areas in my project.
Thanks.
Please merge and publish this PR to npm but with next channel, everyone should be able to install and test it.
https://6253cfb04272251095ef217a--clever-dasik-1a4bcc.netlify.app/
just update @vue/compat, @vitejs/plugin-vue and @vue/compiler-sfc and only use vue3 mode
and only use this patch code, not patch-package module for patch @vue/compat
const originalVModelDynamicCreated = Vue.vModelDynamic.created;
const originalVModelDynamicBeforeUpdate = Vue.vModelDynamic.beforeUpdate;
Vue.vModelDynamic.created = function (el, binding, vnode) {
originalVModelDynamicCreated.call(this, el, binding, vnode);
if (!el._assign) {
el._assign = () => {};
}
};
Vue.vModelDynamic.beforeUpdate = function (el, binding, vnode) {
originalVModelDynamicBeforeUpdate.call(this, el, binding, vnode);
if (!el._assign) {
el._assign = () => {};
}
};
Some components are still not run properly I did not check most of the components, I only saw these problems
- toast-advanced
- popover-advanced
- bootstrap 5.0.0 (current version 5.1.3 and 5.2.0 is coming) use @popper/core not popper.js is deprecated
@sadeghbarati thank you for givin this a try and catching couple bugs! I'm going to take a look ath them soon
However, rewrite to bootstrap v5 is totally separate topic
@xanf
one thing I forgot to mention:
I think the portal-vue force this project to use @vue/compat?
@vue/compat performance is same as vue2&3 ?
Although there is a small performance/size overhead
If your team separate vue 2&3 of bootstrap-vue would be so better because first time I use compat version and Compatibility table seem have many incompatibles and caveats even ssr problem for this project
I do not expect much from you at this time because I am a little aware of the situation you are in now in Ukraine
These are my thoughts ppl can easily contribute and help ur team along:
- bootstrap-vue & vue 2 & portal-vue & bootstrap 4
- bootstrap-vue & vue 2 & portal-vue & bootstrap 5
- bootstrap-vue & vue 3 & builtin Teleport & bootstrap 4
- bootstrap-vue & vue 3 & builtin Teleport & bootstrap 5
How can I help moving this over the finish line? :)
Seems to me that it would be easier to create a separate project for vue3+bootstrap5 instead of creating this frankenstein thing trying to update existing code. Leave the current project as is and don't worry about cross compatibility. That's my 2 cents anyways.
Seems to me that it would be easier to create a separate project for vue3+bootstrap5 instead of creating this frankenstein thing trying to update existing code. Leave the current project as is and don't worry about cross compatibility. That's my 2 cents anyways.
@sdwru, a project has been started: https://github.com/cdmoro/bootstrap-vue-3
Seems to me that it would be easier to create a separate project for vue3+bootstrap5 instead of creating this frankenstein thing trying to update existing code. Leave the current project as is and don't worry about cross compatibility. That's my 2 cents anyways.
What about the people that are already using this on their project?
Seems to me that it would be easier to create a separate project for vue3+bootstrap5 instead of creating this frankenstein thing trying to update existing code. Leave the current project as is and don't worry about cross compatibility. That's my 2 cents anyways.
What about the people that are already using this on their project?
What about them? They can keep on using it.
That mean they are going to stuck with Vue 2 forever. They can't upgrade to Vue 3 due to Bootstrap Vue is not compatible and they can't switch to that new library too due to it is Vue 3.
That mean they are going to stuck with Vue 2 forever. They can't upgrade to Vue 3 due to Bootstrap Vue is not compatible and they can't switch to that new library too due to it is Vue 3.
No it doesn't. It will most likely be just as much work upgrading an existing project to a separate fork of this project than it would be if this project tried to create some frankenstein that tries to be cross compatible. As it stands now there hasn't been much progress going down the current path for over 2 years now. People are moving on. Nobody starting a new project is going to use this.
Well I'm one of the affected users. My current application is large and deeply integrated with Bootstrap Vue and switching it to another framework + Vue 3 in a single shot is a tremendous task. I need this compatible build in order to upgrade my project to Vue 3 as a first step.
Well I'm one of the affected users. My current application is large and deeply integrated with Bootstrap Vue and switching it to another framework + Vue 3 in a single shot is a tremendous task. I need this compatible build in order to upgrade my project to Vue 3 as a first step.
Like I said, it will be just as much work either way. Believe it or don't. If you think that you will be able to just type npm upgrade and your project will magically upgrade everything to Vue3+Bootstrap 5 or even just Vue3, you are sadly mistaken.
Yes it much work but it can be splitting. Just dealing with Vue 2 to Vue 3 is already a huge amount of task. If we don't have this compatible build it need to fix both Vue 3 and framework compatibility issue in a single upgrade. But if we have this compatible build we can just dealing with Vue 3 first without worrying with framework issues.
Let me jump into this discussion and explain my rationale
This "frankenstein" has the only purpose: to allow people iterate while we're rewriting bootstrap-vue to Vue3 (bootstrap 5 will be addressed later)
This frankenstein also serves second, not-so-obvious support - this potentially will be "reference" rendering which will be used to check that rewrite renders the same HTML as original version
I've heard "it's easier to do it from scratch" multiple times in my career (and initiated it multiple times). Unfortunately success rate of such efforts was lower than I wanted. So my strategy is definitely to stand on the shoulder of giants for this rewrite
Let me explain my rationale:
- rewriting Vue2 -> Vue 3 + bootstrap-vue ->
put your favorte alternate implementation herebasically violates main rule "touch one thing at once", because it involves three (Vue3, bootstrap v5 and js). This is pure insanity for any mid- to large-scale project -
bootstrap-vueis feature rich. And implementing all these features is identical in terms of complexity no matter if it is rewrite or "from scratch". But keeping compatibility is far more easier if considered from early start -
bootstrap-vuehas quite good codebase. It might look insane and weird, but it is pretty high quality. I've understood a lot of nuances while working on this MR and now utilizing this knowledge on rewrite
I'm planning to work on this MR release as bootstrap-vue@compat within next 3 days, right now deep into BTable vue3 version
Thanks everyone for patience!
Nobody is suggesting a rewrite from scratch. (-‸ლ)
There is no reason you can't reuse code when it makes sense to do so. There is no reason to believe dropping cross-compatibility will make porting an existing project to new code more difficult either. It's going to be lots of work regardless because Vue3 + Bootstrap 5 have too many changes.
People have been asking for an update for over 2 years now and it's just not happening. The current project is too outdated for anyone to consider it for new projects and there is no indication that is changing anytime soon. Also, the only thing being discussed right now is updating to Vue3. That's still not good enough to compel anyone to use it for a new project. Not until it's updated to Bootstrap 5 and at the current rate of progress there will probably be a Bootstrap 6 by them.
@sdwru thank you for taking time to explain your position. I believe the biggest opensource strength (and, well, sometimes, flaw) is that there are many aprroaches, so people can choose the best one for them
Like I said, I see the bootstrap-vue as powerful library with no match between other clones, so I will continue working on bringing Vue3 and Bootstrap5 to it
Please be patient. @xanf is living in Ukraine, which is currently attacked by Russia.
There appears to be an incompatibility with how Vue 3 handles template refs.
https://github.com/bootstrap-vue/bootstrap-vue/blob/dev/src/components/tabs/tabs.js#L420 https://github.com/bootstrap-vue/bootstrap-vue/blob/dev/src/components/tabs/tabs.js#L592
this.$refs.buttons ends up pointing to a single button (it's not an array as expected) with the Vue 3 compat, specifically when the title slot is used.
<b-tabs>
<b-tab active>
<template #title>
Test
</template>
Hello World
</b-tab>
</b-tabs>

Title attribute is working just fine:
<b-tabs>
<b-tab title="Test" active>
Hello World
</b-tab>
</b-tabs>
There appears to be an incompatibility with how Vue 3 handles template refs.
The v-for Array Refs migration strategy seems to work, for my fork of this branch, with the b-time component which relies on using ref in v-for similarly to b-tabs.
It is pretty straightforward to implement, but not sure if there are any pitfalls to the approach. I haven't yet tested this in a build.
Inherited attributes don't appear to be working on at least some components, like <b-alert>.
Template:
<b-alert data-foo="bar"></b-alert>
Actual Rendered Markup:
<div class="alert" role="alert" aria-live="polite" aria-atomic="true""></div>
Expected Rendered Markup:
<div class="alert" role="alert" aria-live="polite" aria-atomic="true"" data-foo="bar"></div>
The getInstanceFromVNode method requires a development build of Vue 3 compat in order to work. Otherwise, you'll run into issues like popovers not closing.
https://github.com/bootstrap-vue/bootstrap-vue/blob/vue3-compat-build/src/utils/get-instance-from-vnode.js#L4
The library will need to be reorganized to not rely on this function.
@mattsteve whoa, thank you. I was considering that, but probably failed in my testing (I'm about getInstanceFromVNode)
regarding inherited attributes - that should be easy fixable
Status update
I understand there is not that much movement here. It's hard for me to find balance between war-related activities (sigh), work and opensource. However I hope to release this MR this month (huge cudos for everyone who is testing)
Also I'm working on finalizing BTable (entire one with all features). No code to share yet, just a spoiler:

I will release this as soon as BTable will be functional (hope to do this month also). I estimate this as one of the hardest (not the biggest, hello calendar, but hardest) component for rewrite :)
@xanf Is there any progress? If not, I plan to use my spare time to develop a new version for vue3 based on this repository
@see https://github.com/bootstrap-vue/bootstrap-vue/pull/6905#issuecomment-1149083451 is from vue3 version of BTable :), not compat one :)