Skip to content

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
  • PlayVueRouterProvider renderer-based integration with Vue Router
  • Shell-driven rendering via PlayRenderer with 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:

Terminal window
npm install
npm run dev -w packages/play-vue-router/examples/demo

Visit http://localhost:5173.

Step-by-Step Code Flow

Use this order to understand how the demo is wired:

  1. src/main.ts creates the actor via definePlayer({ machine: authMachine, catalog }).
  2. src/router.ts installs one catch-all route; PlayRenderer selects the actual view from actor state.
  3. src/App.vue uses PlayVueRouterProvider + createRouteMap(authMachine) with a renderer function to connect router navigation and actor routing events.
  4. Provider waits for router.isReady() so direct URL loads are handled correctly.
  5. src/Shell.vue renders actor-projected state (PlayRenderer) and sends events back to the actor.
  6. 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 mount
  • src/router.ts - single catch-all route record
  • src/App.vue - provider wiring and renderer composition
  • src/Shell.vue - actor-driven demo shell with nav, renderer, and debug panel
  • src/components/ - route and page view components
  • test/browser/startup.browser.test.ts - startup check for home + login link rendering
  • test/browser/auth-flow.browser.test.ts - login -> dashboard -> profile -> logout browser flow
  • test/reactivity.test.ts - reactive integration assertions

State Machine & Architecture Details

The demo utilizes XMachines architectural invariants:

  1. Actor Authority: When a user navigates to a protected route via a link, Vue Router updates the location. The PlayVueRouterProvider intercepts this, translates it to a play.route event, and sends it to the actor. The actor evaluates guards (e.g. isAuthenticated) and transitions.
  2. 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.
  3. Signal-Only Reactivity: The bridge leverages Vue’s watch and triggerRef internally 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:

  1. notify
  2. queueMicrotask
  3. getPending()
  4. Read actor signals and sync Vue-local render state
  5. 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

Terminal window
npm run dev # Start Vite dev server (default: http://localhost:5173)
npm run build # Build production assets
npm run preview # Preview production build locally
npm run test # Run unit/integration tests via Vitest
npm run test:browser # Run browser-mode tests via vitest.browser.config.ts

Verification

Use these checks from this directory:

Terminal window
npm run test
npm run test:browser

Manual sanity check:

  1. Start with npm run dev.
  2. Open http://localhost:5173.
  3. Confirm login/logout transitions update both view and URL, and that accessing protected routes while logged out redirects properly.

Learn More