I Did Not Migrate a Vue App. I Re-Architected an 11-Package Ecosystem.
What moving WBC-UI from Vue 2 to Vue 3 taught me about dependency graphs, compatibility, documentation, and evidence-driven AI engineering

Most migration stories begin with one application.
Mine began with a runtime, ten dependent products, two generations of Vuetify, multiple documentation applications, a CLI, build pipelines, package contracts, and users who should not need to understand the entire dependency graph before rendering one component.
That distinction changed the work.
Replacing Vue 2 imports with Vue 3 imports was the easy part. The difficult part was deciding what the platform still meant after the migration:
- Which behaviors were public contracts, even when they were undocumented?
- Which Vue 2 patterns had to disappear rather than be emulated?
- In what order could packages move without validating against a broken foundation?
- How could the old line remain usable while the new line evolved?
- What evidence would demonstrate that a package was publishable—not merely compilable?
This is the story of migrating WBC-UI from the Vue 2 @wbc-ui2 line to the Vue 3 @wbc-ui line. It is also the story of why I built wb-flow: once a system becomes large enough, the workflow used to change it becomes part of the architecture.
The result at a glance
The Vue 3 line currently contains 11 coordinated MIT-licensed packages:
@wbc-ui/core@wbc-ui/code@wbc-ui/dataviewer@wbc-ui/js@wbc-ui/press@wbc-ui/pay@wbc-ui/latex@wbc-ui/mermaid@wbc-ui/chart@wbc-ui/alert@wbc-ui/cli
The current source line is 3.0.0-rc.2. “RC” matters: the migration has reached a release-candidate platform, not an excuse to call unfinished work stable.
The packages are presented through one searchable hub at https://wbc-ui.com, while the Vue 2 line remains available as legacy history and a migration reference.

Figure 1 — The current WBC-UI Vue 3 product surface. Core is the rendering spine; the other packages either extend that runtime or provide tooling around it.
Why this was not “upgrade the dependencies”
WBC-UI is built on a configuration-driven idea: an interface can be represented as data.
Instead of assembling every component tree manually, the WBC runtime accepts strings, arrays, objects, files, functions, and primitives, determines what they represent, and produces Vue nodes. The specialized packages apply that model to data exploration, documentation, charts, diagrams, code, mathematics, alerts, and other interfaces.
That means the core contract is not only a set of component props. It is a language.
A language migration has more failure modes than a component migration:
- Object shapes can still parse while changing meaning.
- A wrapper element can make HTML invalid without producing a compile error.
- Reactivity can appear correct until a descriptor contains a function of component context.
- A package can build in isolation while importing the wrong form of a peer package.
- A documentation page can render while search indexes only headings and silently ignores content.
- A chart or diagram can display while its composition API no longer exposes the extension point that made it valuable.
The migration therefore had four simultaneous targets:
- Runtime compatibility — preserve the useful descriptor model.
- Vue 3 correctness — adopt Vue 3 rendering, reactivity, lifecycle, and plugin semantics rather than simulating Vue 2.
- Package independence — each package should remain understandable and installable on its own.
- Ecosystem coherence — the packages should still feel like one platform.
The dependency graph determined the schedule
The correct migration order was not the directory order and not the order of the most visually impressive demos.
It was the dependency graph.

Figure 2 — A simplified migration sequence. Packages in the same wave can progress independently; downstream packages wait for the contracts they consume.
Wave 0 — inventory and contracts
Before migrating code, I needed an inventory:
- package entry points;
- peer dependencies;
- public props and events;
- build outputs;
- documentation routes;
- Vue 2 behaviors that callers already depended on;
- tests that existed;
- tests that should have existed.
This is less exciting than rewriting a renderer, but it prevents a migration from becoming an archaeological project halfway through.
Wave A — the rendering spine and independent tooling
@wbc-ui/core had to move first because almost every runtime package depends on it.
@wbc-ui/cli could progress independently because its job is scaffolding and project health rather than runtime rendering.
Wave B — direct core consumers
Once the core contract was usable, the direct extensions could migrate in parallel:
- code;
- JavaScript view runtime;
- LaTeX;
- Mermaid;
- chart;
- pay;
- press.
Parallel does not mean uncoordinated. It means the tasks share a stable upstream contract and can be validated independently.
Wave C — packages with deeper internal dependencies
@wbc-ui/dataviewer depends on both core and code. Its migration had to wait until those foundations were credible.
@wbc-ui/alert consumes core and dataviewer, so it sits further downstream.
Wave D — the ecosystem surface
Only after individual packages were usable did the central hub become a reliable representation of the whole system. Search, navigation, cross-package links, migration guidance, and release positioning belong to the product—not to cleanup after the product.
The package-by-package migration
The following sections are intentionally short. The goal is not to reproduce eleven documentation sites inside one article. It is to show what each package contributed to the migration and why the ecosystem is more than a list of wrappers.
1. WB Core — preserve the language, replace the assumptions

Core is the rendering spine. It identifies input forms, dispatches them to specialized renderers, decorates the resulting nodes, and returns Vue output.
The migration forced a separation between the durable idea and the Vue 2 implementation details. The durable idea was “UI as data.” The replaceable details were listener placement, attribute forwarding, component resolution, vnode construction, lifecycle hooks, and other framework-generation assumptions.
Core also had to retain dynamic descriptors: with reactive evaluation enabled, a property can be a function of component context rather than a frozen value. That capability is essential to WBC-UI's identity, but it must remain explicit enough to debug.
2. WB Code — a technical product needs inspectable output

WB Code provides syntax-aware presentation and live technical examples. During a platform migration, code display is not decoration. It is how users compare the descriptor they supplied with the interface they received.
The Vue 3 version therefore became part of the platform's explainability surface: examples, copyable configuration, and playground behavior had to follow the same contracts as the packages they document.
3. WBDataViewer — recursive data became the stress test

WBDataViewer was the most demanding consumer of the runtime because it combines recursion, valid table structure, nested configuration, editing, searching, filtering, projection, and context-dependent behavior.
The migration exposed problems that simple component demos do not:
- invalid wrapper elements inside native tables;
- nested objects competing for width;
- local versus deep search scope;
- configuration precedence between root props, path configuration, project configuration, and object-embedded configuration;
- reactive descriptor functions through
tbDive; - preserving the active value while changing another prop in a live laboratory.
This package became a practical compatibility suite for the core runtime.
4. WBJS — configuration-driven views above individual components

WBJS takes the configuration-driven model from a component tree to an application view. It turns structured view data into Vue behavior and coordinates lifecycle-oriented configuration around the WBC renderer.
Its migration was a reminder that platform architecture exists at more than one level. A renderer can be correct while the view lifecycle that feeds it is stale.
5. WBPress — documentation became a product dependency

WBPress powers the documentation applications, including the WBC-UI hub itself. Migrating it meant migrating the system used to explain every other migration.
The Vue 3 work expanded beyond page rendering: content graphs, sidebar routing, Markdown and WBC content, full-page search, static metadata, feeds, and deployable starters all became part of release readiness.
This is dogfooding with consequences. If WBPress navigation truncates or search sees only titles, the defect affects the public surface of the entire ecosystem.
6. WBPay — keep integration boundaries narrow

WBPay uses WBC descriptors to present payment and support destinations. Its value is not replacing payment providers. It is keeping provider-facing UI composable within the same rendering language.
The migration reinforced a useful package rule: integrations should depend on stable local contracts and expose provider variation without leaking it into every consumer.
7. WBLaTeX — specialized rendering, shared composition

WBLaTeX wraps mathematical rendering in the same compositional environment. It is deliberately focused: receive mathematical source, render it predictably, and remain compatible with the surrounding WBC layout.
Small packages are valuable migration checkpoints. If a focused extension is difficult to migrate, the core extension contract is probably too complicated.
8. WBMermaid — static diagrams became live surfaces

The Vue 3 WBMermaid work did more than restore Mermaid rendering. It introduced asWbc, allowing selected diagram leaves to become live WBC/Vue content with dynamic behavior and events.
That feature represents the direction of the ecosystem: established rendering engines remain responsible for what they do well, while WBC supplies a controlled composition layer around or inside their output.
9. WBChart — use ECharts, do not pretend to replace it

WBChart is source-first Apache ECharts integration for Vue 3. It accepts object data, remote JSON, and GeoJSON while allowing optional WBC composition through wbcObj.
The migration clarified the positioning: WBChart is not a new charting engine. Its value lies in source handling, Vue lifecycle integration, events, and composability with the rest of WBC-UI.
10. WBAlert — downstream packages reveal ecosystem coupling

WBAlert depends on core, dataviewer, Vue, and Vuetify. That makes it a downstream validation point: a successful alert build says something about several upstream contracts, not only one component.
This is why dependency-aware sequencing matters. Migrating the package early would have produced false debugging signals from unstable dependencies.
11. WBCLI — migration must be reproducible for the next project

WBCLI turns the ecosystem's expected project structure into commands: initialize, create, diagnose, update, and guide local development.
The Vue 3 CLI is not a copied Vue 2 launcher. It reflects Vite-era project assumptions, Vue 3 health checks, safe same-major updates, scriptable initialization, and current package sources. The WBC-UI hub now carries its full documentation rather than treating the CLI as an undocumented side tool.
The hardest changes were at the boundaries
Across the packages, four boundary problems required the most judgment.
1. Render correctness versus visual similarity
A page can look approximately correct while its DOM is invalid. Native tables made this visible: stray wrapper spans between tr, th, and td elements broke alignment even when the browser attempted to repair the structure.
The lesson is general: migration acceptance cannot be “the screenshot looks close.” Structural semantics, accessibility, events, and reactivity require separate checks.
2. Configuration precedence
WBC-UI enables configuration at several levels. WBDataViewer alone can receive root props, path-specific configuration, project-item configuration, shorthand configuration keys, and object-embedded configuration.
Flexibility without precedence rules becomes unpredictability. The migration therefore had to make the hierarchy explicit and document which layer wins.
3. Packaging is part of the API
Source code is only one release artifact. A package also exposes:
- entry points;
- module formats;
- type declarations;
- peer dependency expectations;
- files included in the npm archive;
- side-effect and tree-shaking behavior;
- install and import examples.
A local monorepo can hide packaging defects because source aliases resolve files that an npm consumer will never receive. Dry-run package inspection and consumer-style tests are not optional release ceremony.
4. Documentation must be validated like code
The documentation applications uncovered failures that package tests could not:
- sidebar content parsed as text instead of WBC descriptors;
- search indexes containing titles but not body content;
- interactive labs losing the current object when one prop changed;
- hidden routes that existed but were not discoverable;
- responsive previews clipped by their own containers.
Documentation is executable product behavior when users depend on it to learn an API.
Why wb-flow became part of the migration story
I maintain the ecosystem independently. At this scale, a long chat transcript is not a project-management system, and an agent saying “done” is not verification.
wb-flow introduced a file-driven loop:
- Audit the current state.
- Convert findings into explicit plan rows.
- Record dependencies and derive executable waves.
- Route planning, implementation, and validation to appropriate model classes.
- Execute independent work in parallel.
- Require the validator to inspect artifacts and repository state.
- Recompute the next executable sequence after every plan-changing action.
The most important rule is simple:
The model that implements a task should not be the only model that decides whether the task is valid.
This does not make AI output automatically trustworthy. It makes failure inspectable.
The workflow also changed my own role. Instead of asking one agent to “migrate the ecosystem,” I had to define contracts, isolate tasks, specify verification, resolve conflicting recommendations, and decide which evidence was sufficient. Agentic engineering increased the need for architecture and judgment; it did not remove it.
What I would do differently
Start with public contracts, not source folders
I would write the compatibility matrix earlier: input, output, props, events, documented behavior, accidental behavior, and intended change. Source structure is not the same thing as product contract.
Establish consumer fixtures before migration
Package tests often import from source. A small external fixture that installs the packed artifact catches a different class of defects.
Treat TypeScript completion as an ecosystem gate
Type coverage should not vary silently from package to package. It is easier to establish the declaration policy before eleven packages reach release-candidate status.
Recruit external users before declaring stability
The current line is an RC because independent use matters. My own documentation applications provide dogfooding, but they do not replace external adoption.
Measure before making performance claims
Vue 3 and modern tooling may improve some paths, but a migration article should not turn architectural confidence into invented benchmarks. Publish numbers only after measuring repeatable workloads.
What this project demonstrates about my work
The visible output is a Vue 3 ecosystem. The deeper engineering work is broader:
- decomposing a platform by dependency rather than by directory;
- preserving an idea while replacing its implementation assumptions;
- designing package boundaries and compatibility rules;
- treating tests, types, archives, documentation, and demos as release artifacts;
- using AI agents without delegating final judgment to them;
- maintaining a legacy line while building a modern one;
- explaining the system so another developer can evaluate it.
That is the kind of work I want to continue: developer tools, developer experience, platform engineering, configuration-driven systems, and evidence-based agentic workflows.
Explore the work
- WBC-UI hub: https://wbc-ui.com
- wb-flow: https://flow.wbc-ui.com
- WBC-UI packages: https://www.npmjs.com/org/wbc-ui
- Source organization: https://github.com/wbc-ui
- Personal GitHub: https://github.com/wissemb11
If you have migrated a multi-package Vue or frontend platform, I would be interested in the compatibility problem that surprised you most—not the syntax change, but the contract you discovered only after something downstream broke.
Status note
This article deliberately describes the Vue 3 line as a release candidate. I will change that
wording only after the stable release is publicly available, and I will add adoption or performance
figures only when they come from repeatable measurements.
</div>