Vue Router Demo
Examples / @xmachines/play-vue-router-demo
Vue Router integration demonstrating Play Architecture with Vue Composition API.
What This Demonstrates
- Shared auth machine reused without framework-specific business logic
PlayVueRouterProviderrenderer-based integration with Vue Router- Shell-driven rendering via
PlayRendererwith actor-authoritative navigation - Vue Composition API mapping to TC39 Signals lifecycle
- Non-browser invariant tests plus browser E2E coverage
Running the Demo
From the repository root:
npm installnpm run dev -w packages/play-vue-router/examples/demoVisit http://localhost:5173.
Step-by-Step Code Flow
Use this order to understand how the demo is wired:
src/main.tscreates the actor viadefinePlayer({ machine: authMachine, catalog }).src/router.tsinstalls one catch-all route;PlayRendererselects the actual view from actor state.src/App.vueusesPlayVueRouterProvider+createRouteMap(authMachine)with a renderer function to connect router navigation and actor routing events.- Provider waits for
router.isReady()so direct URL loads are handled correctly. src/Shell.vuerenders actor-projected state (PlayRenderer) and sends events back to the actor.- Browser tests in
test/browser/validate startup and auth route transitions.
// src/main.ts (shape)const createPlayer = definePlayer({ machine: authMachine, catalog });const actor = createPlayer();actor.start();
const app = createApp(App);app.provide("actor", actor);app.use(router);app.mount("#app");// src/router.ts (shape)export const routes = [{ path: "/:pathMatch(.*)*", name: "xmachines-play", component: RouteHost }];// src/App.vue (renderer shape)const renderShell = (currentActor, currentRouter) => h(Shell, { actor: currentActor, router: currentRouter, });<!-- src/components/Login.vue (shape) --><script setup lang="ts">const actor = inject("actor")!;const login = () => actor.send({ type: "auth.login", username: "demo" });</script>Key Files
src/main.ts- actor creation/start and app mountsrc/router.ts- single catch-all route recordsrc/App.vue- provider wiring and renderer compositionsrc/Shell.vue- actor-driven demo shell with nav, renderer, and debug panelsrc/components/- route and page view componentstest/browser/startup.browser.test.ts- startup check for home + login link renderingtest/browser/auth-flow.browser.test.ts- login -> dashboard -> profile -> logout browser flowtest/reactivity.test.ts- reactive integration assertions
State Machine & Architecture Details
The demo utilizes XMachines architectural invariants:
- Actor Authority: When a user navigates to a protected route via a link, Vue Router updates the location. The
PlayVueRouterProviderintercepts this, translates it to aplay.routeevent, and sends it to the actor. The actor evaluates guards (e.g.isAuthenticated) and transitions. - Passive Infrastructure: The router does not execute Vue route guards for business logic. The actor dictates whether navigation is permitted. The Vue application only renders the state.
- Signal-Only Reactivity: The bridge leverages Vue’s
watchandtriggerRefinternally to react precisely when signals update, without polluting the Vue component tree with reactive refs that hold business state.
Watcher Lifecycle and Cleanup Contract
This demo uses the canonical watcher lifecycle:
notifyqueueMicrotaskgetPending()- Read actor signals and sync Vue-local render state
- Re-arm with
watch()/watch(...signals)
Notifications are one-shot, so re-arm is required. Teardown is explicit: provider/bridge cleanup must flow through disconnect and watcher unwatch, not GC-only assumptions. The PlayVueRouterProvider wires this seamlessly into the component’s onUnmounted hook.
Adapter Boundaries
Vue Router integration remains passive infrastructure. RouterBridgeBase owns shared synchronization policy and the Vue adapter is a thin framework port. Route extraction override strategies remain supported when teams need custom route-name mapping.
Available Scripts
npm run dev # Start Vite dev server (default: http://localhost:5173)npm run build # Build production assetsnpm run preview # Preview production build locallynpm run test # Run unit/integration tests via Vitestnpm run test:browser # Run browser-mode tests via vitest.browser.config.tsVerification
Use these checks from this directory:
npm run testnpm run test:browserManual sanity check:
- Start with
npm run dev. - Open
http://localhost:5173. - Confirm login/logout transitions update both view and URL, and that accessing protected routes while logged out redirects properly.
Learn More
- Vue Router Adapter README - Package-level API and bridge docs