Skip to content

Solid Router Demo

Examples / @xmachines/play-solid-router-demo

Solid + @solidjs/router demonstration of the Play architecture using provider-based router integration.

What This Demonstrates

  • Shared auth machine reused without framework-specific business logic
  • PlayRouterProvider renderer-based integration with Solid Router
  • Shell-driven rendering via PlayRenderer with actor-authoritative navigation
  • Canonical TC39 Signals lifecycle integration with Solid’s fine-grained reactivity
  • Non-browser invariant tests plus browser E2E coverage

Running the Demo

From this directory (packages/play-solid-router/examples/demo):

Terminal window
npm install
npm run dev

Open http://localhost:5173.

Step-by-Step Code Flow

Use this order to understand the implementation:

  1. src/main.tsx bootstraps the demo and mounts the <App /> component.
  2. src/App.tsx wires actor lifecycle, shared machine/catalog imports, and router integration.
  3. A routeMap is created by extracting routes from the shared authMachine.
  4. PlayRouterProvider forwards navigation intent from @solidjs/router to the actor, and reflects actor-approved routes back to the URL.
  5. Shell (inside src/App.tsx) renders actor-projected views (PlayRenderer) and provides the shared UI shell.
  6. test/library-pattern.test.ts and test/browser/ verify invariants and runtime routing behavior.
// src/main.tsx (shape)
render(() => <App />, document.getElementById("root")!);
// src/App.tsx (shape)
const createPlayer = definePlayer({ machine: authMachine, catalog });
const actor = createPlayer();
actor.start();
const routeMap = createRouteMap(authMachine);
<PlayRouterProvider
actor={actor}
routeMap={routeMap}
router={{ navigate, location, params }}
renderer={(currentActor, currentRouter) => (
<Shell actor={currentActor} router={currentRouter} />
)}
/>;
// src/components/Nav.tsx (shape)
<button onClick={() => actor.send({ type: "auth.logout" })}>Logout</button>

Key Files

  • src/App.tsx - Integration entry point wiring actor, provider, renderer, and shared machine/catalog imports.
  • src/main.tsx - Vite bootstrap for the Solid app.
  • src/components/ - Demo views and controls that send events to the actor.
  • test/library-pattern.test.ts - Node/Vitest invariant checks for architecture boundaries.
  • test/browser/startup.browser.test.ts - Browser startup and mount coverage.
  • test/browser/auth-flow.browser.test.ts - Browser auth and routing behavior coverage.

State Machine & Architecture Details

The demo utilizes XMachines architectural invariants:

  1. Actor Authority: When a user navigates to a protected route, the Solid Router updates the URL. The PlayRouterProvider intercepts this, translates it to a play.route event, and sends it to the actor. The actor evaluates guards (e.g., is the user logged in?) and transitions to the appropriate state.
  2. Passive Infrastructure: Solid components hold no business state. They observe actor.currentView to know what to render, and actor.currentRoute to reflect the active path.
  3. Signal-Only Reactivity: Instead of React-like re-renders, Solid uses createEffect internally within the RouterBridge to react precisely when signals update.

Watcher Lifecycle and Cleanup Contract

This demo follows the canonical watcher lifecycle:

  1. notify
  2. queueMicrotask
  3. getPending()
  4. Read actor signals and update Solid-local render triggers
  5. Re-arm with watch()/watch(...signals)

Watcher notifications are one-shot. Cleanup is explicit and lifecycle-bound: Solid teardown uses onCleanup, and provider/bridge teardown must call disconnect/unwatch paths rather than relying on GC-only cleanup. This is crucial to prevent memory leaks during component unmounting.

Adapter Boundaries

Solid Router integration remains passive infrastructure. RouterBridgeBase stays the shared policy point; the Solid adapter only implements framework port behavior (calling navigate() and tracking location.pathname).

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 the app 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