Skip to content

Migrating from v2

Upgrade in a safe order, handle inverted boolean defaults, and audit changes the compiler cannot find.

On this page

Most of a v2 → v3 upgrade is work the compiler does for you. With typed templates, renamed props, removed props and unregistered variants all fail loudly, component by component.

The part worth reading carefully is the other one: a set of boolean defaults flipped, two visual defaults changed, and neither the type-checker nor the console will mention it.

Do it in this order

The order matters. Steps 1 and 2 have to land before anything renders correctly, and step 3 has to land before a visual review means anything.

  1. Swap the setup. Install the build plugin and the runtime plugin. Nothing renders correctly until this is done.
  2. Move your config. Everything you passed to useBbConfig().setConfig() except locale now lives in the build-plugin options.
  3. Re-base your theme. Token names and defaults changed, including the primary colour itself.
  4. Fix what the compiler finds. Renamed props, removed props, unregistered variants. Work component by component.
  5. Audit what the compiler cannot find. Untyped templates, v-bind objects, and behavioural flips are silent. Run the grep pass.
  6. Convert class overrides to variants. A userland class="bb-button--outline" keeps working, but the variant prop is typed and is the only form the registry validates.

The plugin pattern

v3 is a plugin, not a bag of exports. Two pieces are always required: a build plugin that generates the config, icon registry and typed variant lists, and a runtime plugin that injects styles and registers the directives. Installation covers the wiring for Vite, Nuxt and Inertia.

Config moves to build time

Everything that used to go through useBbConfig().setConfig() is now a build-plugin option. Only locale stays runtime-editable, through useBbConfig().

This is the change that makes the variant registries and the typed icon names possible: the plugin knows at build time what names exist, so an unregistered one is a compile error rather than an unstyled element.

Re-base your theme

The token layer also shrank — 91 properties, down from around 140 — and the old --x-light / --x-dark / --x triple is gone. One name per token; .dark re-declares only the knobs that change.

Re-declare your brand theme before you look at a single screen. Design tokens is the new architecture, and the theme builder is the fastest way to get a starting block.

The polarity wave

The v3 rule: a boolean prop defaults to false, and its name states what turning it on does. Props that defaulted to true were renamed to their inverse.

So if you passed the old prop at its default (:show-close="true"), delete it. If you passed the non-default (:show-close="false"), switch to the new name (hide-close).

Component(s)v2 (default)v3 (default)
BbAlert, BbDialog, BbOffCanvasshowClose (true)hideClose (false) — also on toast() options
BbTableallowSelectAll (true)disableSelectAll (false)
BbTabsanimateX / animateY (true)disableAnimateX / disableAnimateY (false)
BbButtonautoLoading (false)disableAutoLoading (false) — silent behaviour flip
BbDropdownButtonautoLoading (true)disableAutoLoading (false)
BbDropdownButtondisabledWhileLoading (true)removed — always disabled while loading
BbSliderthumbTranslate (true)disableThumbTranslate (false)
BbColorPalette, BbSelectPopoverflip (true)disableFlip (false)
BbSelect, BbSelectPopover, BbDatePickerInputallowWriting (true)disableWriting (false) — allow-writing="not-mobile" becomes disable-writing="mobile"
BbCheckbox, BbRadio, BbSwitchreverse (true)reverse (false, meaning inverted)
BbAlertariaAtomic (true)removed — correct live-region behaviour is hardcoded
BbChipclearable (true)removed with the component; BbBadge.clearable defaults false

Two of these change behaviour rather than only spelling. BbButton now shows the loading spinner and disables itself for an async @click handler; pass disable-auto-loading to keep the v2 behaviour. And reverse on BbCheckbox, BbRadio and BbSwitch means the opposite of what it did: unset renders identically, but a v2 :reverse="false" becomes a bare reverse, and a v2 reverse is deleted.

Variants became typed

In v2, theme took any string and a class override did the rest. In v3, variant names are typed per family, and a name that is neither built-in nor registered through the plugin fails to compile.

Registering a name is first-class and expected — see Design language for the plugin option and the stylesheet block that go with it. Both halves are required: a registered name with no CSS is an unstyled pill, and CSS with no registration fails vue-tsc.

The silent-failure audit

Everything the compiler cannot catch, in one pass. Run each in the consumer project; every hit needs a decision, not necessarily a change.

Two surfaces have no other net. Composable option objects live in .tstoast({ … }) and confirm({ … }) inside stores, interceptors and helpers — and neither gate reaches them: npx bitboss-ui check opens only .vue and .md, and the eslint plugin walks template ASTs. Nothing reads CSS at all — not the CLI, not eslint, not vue-tsc, not the browser console.

bash
# Props, slots and markup
rg -in 'querykey'                         # → queryKey (URL params silently move)
rg -n 'show-close|showClose'              # → hide-close, or delete on popover/tooltip
rg -n 'allow-select-all|allowSelectAll'   # → disable-select-all, inverted
rg -n 'auto-loading|autoLoading'          # BbButton behaviour flip / rename
rg -n 'disabled-while-loading|disabledWhileLoading'   # removed
rg -n 'animate-x|animate-y|animateX|animateY'         # → disableAnimate*
rg -n 'thumb-translate|thumbTranslate'    # → disable-thumb-translate
rg -n ':flip=|flip="'                     # → disable-flip
rg -n 'hide-arrow|show-arrow|arrow-padding'           # removed off tooltips
rg -n '<BbChip'                           # removed → BbBadge clearable
rg -n '<BbBadge'                          # v2 badges are v3 indicators — review each
rg -n '<BbTab\b'                          # → BbTabs
rg -n '#label-'                           # BbTabs label slot → #label:<slotKey>
rg -n 'label-position|labelPosition'      # groups → legend-position
rg -n 'prefill="focus"'                   # → 'interaction'
rg -n ':prefill="false"'                  # now search-first, not load-on-open
rg -n 'allow-writing|allowWriting'        # → disable-writing, inverted
rg -n 'filter-by|filterBy'                # function/string → string[]
rg -n 'option:prepend|option:append|#chevron'         # removed slots
rg -n 'disabled:' --type ts --type vue    # item.disabled → the selectable predicate

# Composable option objects (.ts / .js)
rg -n 'timeout' --type ts --type js       # toast()/confirm() options → duration
rg -n 'theme' --type ts --type js         # toast()/confirm() options → variant
rg -n 'dismissAll'                        # useToast().dismissAll() → dismiss()
rg -n 'autoClose|auto-close'              # confirm(): opt-in in v2, default true in v3
rg -n 'yesText|noText|onYes|onNo'         # collapsed into the yes/no configs
rg -n "provide\('icons'"                  # shadowed by the plugin's registry

rg -n 'reverse' --type ts --type js deserves its own line: the checkbox, radio and switch flip is the one change in this list that renders identically until someone passed the prop explicitly.

Where the rest lives

This page is the shape of the upgrade. The detail is on the component pages, where you will actually be standing when you hit it:

  • Every component's Examples page carries its v2 notes inline, folded into a "Coming from v2" block beside the behaviour they concern.
  • Every component's API page ends with a generated Changes from v2 table — props removed or renamed in 3.0, and which ones fail silently.

Start from the component the compiler pointed at, not from here.

Once the compiler is clean, run npx bitboss-ui check over the migrated .vue and .md files, then repeat the grep audit above for composable options and CSS. Finish with a visual pass only after the theme tokens are in place. This order keeps type errors, silent API changes and intentional visual changes as three separate review steps.