@xmachines/play-tanstack-solid-router
Documentation / @xmachines/play-tanstack-solid-router
TanStack Solid Router adapter for XMachines Universal Player Architecture
Signals-native integration with TanStack Solid Router enabling logic-driven navigation through Solid.js reactivity.
Overview
@xmachines/play-tanstack-solid-router provides seamless integration between TanStack Solid Router and XMachines state machines. Built on Solid’s reactive primitives, it implements the RouterBridgeBase pattern for bidirectional synchronization while remaining framework-swappable.
Per RFC Play v1, this package implements:
- Actor Authority (INV-01): State machine controls navigation, router reflects decisions
- Passive Infrastructure (INV-04): Router observes
actor.currentRoutesignal - Signal-Only Reactivity (INV-05):
createEffectsynchronizes URL with actor state
Key Benefits:
- Signals-native: Zero adaptation layer between Solid signals and TC39 Signals
- Bridge-first: Extends shared
RouterBridgeBasepolicy used by other adapters - Automatic tracking:
createEffecthandles dependency tracking (no manual Watcher setup needed for the bridge) - Logic-driven navigation: Business logic in state machines, not components
- Type-safe parameters: Route params flow through state machine context
Framework Compatibility:
- TanStack Solid Router 1.100.0+
- SolidJS 1.8.0+
- TC39 Signals polyfill integration
Installation
npm install @tanstack/solid-router@^1.108.0 solid-js@^1.8.0 @xmachines/play-tanstack-solid-router @xmachines/play-solidPeer dependencies:
@tanstack/solid-router^1.108.0 - TanStack Solid Router librarysolid-js^1.8.0 - SolidJS runtime@xmachines/play-solid- Solid renderer (PlayRenderer)@xmachines/play-actor- Actor base@xmachines/play-router- Route extraction@xmachines/play-signals- TC39 Signals polyfill
Quick Start
import { Router, createRouter } from "@tanstack/solid-router";import { PlayTanStackRouterProvider, createRouteMap } from "@xmachines/play-tanstack-solid-router";import { PlayRenderer } from "@xmachines/play-solid";import { definePlayer } from "@xmachines/play-xstate";import { routeTree as routerRouteTree } from "./routeTree.gen"; // from TanStack
function App() { // 1. Create player with state machine const createPlayer = definePlayer({ machine: authMachine, catalog: componentCatalog, }); const actor = createPlayer(); actor.start();
// 2. Create TanStack router instance const router = createRouter({ routeTree: routerRouteTree });
// 3. Create route mapping from machine routes const routeMap = createRouteMap(authMachine);
return ( // 4. Wrap with provider to sync actor and router <PlayTanStackRouterProvider actor={actor} router={router} routeMap={routeMap} renderer={(currentActor, currentRouter) => ( <Router router={currentRouter}> <PlayRenderer actor={currentActor} components={components} /> </Router> )} /> );}API Reference
SolidRouterBridge
Router adapter implementing the RouterBridge protocol for TanStack Solid Router.
Type Signature:
class SolidRouterBridge { constructor(router: Router, actor: AbstractActor<any>, routeMap: RouteMap); dispose(): void;}Constructor Parameters:
router- TanStack Solid Router instanceactor- XMachines actor instancerouteMap- Bidirectional state ID ↔ path mapping
Methods:
connect()- Start bidirectional synchronization.disconnect()- Stop synchronization and cleanup bridge resources.dispose()- Alias ofdisconnect().
Internal Behavior:
- Uses
RouterBridgeBaseTC39 watcher lifecycle for actor→router synchronization - Updates TanStack Router via
router.navigate({ to: path })when actor state changes - Uses
router.subscribeto watch history navigation events - Sends
play.routeevents to actor when user navigates
PlayTanStackRouterProvider
A Solid component that automatically sets up, connects, and tears down the SolidRouterBridge using Solid’s lifecycle.
interface PlayTanStackRouterProviderProps { actor: AbstractActor<any>; router: Router; routeMap: RouteMap; renderer: (actor: AbstractActor<any>, router: Router) => JSX.Element;}Props:
actor- The XMachines player actorrouter- The TanStack Solid Router instancerouteMap- Mapping between paths and state IDsrenderer- A render prop function that receives the active actor and router
Behavior:
- Instantiates
SolidRouterBridgeon mount - Calls
bridge.connect() - Renders the content returned by the
rendererfunction - Calls
bridge.disconnect()when the component unmounts viaonCleanup
createRouteMap()
Helper to build a RouteMap instance directly from an XState machine.
Signature:
function createRouteMap(machine: AnyStateMachine): RouteMap;Usage:
import { createRouteMap } from "@xmachines/play-tanstack-solid-router";
const routeMap = createRouteMap(machine);Usage Patterns
Dynamic Routes with Parameters
TanStack Router and URLPattern (used internally) support dynamic route matching syntax:
// Machine configurationconst machineConfig = { states: { profile: { meta: { route: "/profile/:userId", view: { component: "Profile" }, }, }, },};
// Route mapping will natively support URLPattern parametersrouteMap.getStateIdByPath("/profile/123"); // → '#profile'Protected Routes and Guards
With XMachines, auth guards are handled entirely inside the state machine, preventing flashes of unauthorized content.
// Machine sidedashboard: { meta: { route: "/dashboard", view: { component: "Dashboard" } }, always: { guard: ({ context }) => !context.isAuthenticated, target: "login" }}When a user navigates to /dashboard:
- TanStack Router updates location
- Bridge intercepts and sends
play.route - Actor evaluates guard -> denies target, transitions to
logininstead - Bridge observes new actor state (
/login) - Bridge tells TanStack Router to redirect to
/login
Circular Update Prevention
The RouterBridgeBase architecture prevents infinite loops between router and actor using two mechanisms:
lastSyncedPathtracking: Stores the last synchronized path to prevent redundant navigations back to the identical location.isProcessingNavigationflag: Set during a router event, preventing the router from immediately reacting to the actor’s synchronous state update.
Comparison with @solidjs/router Adapter
| Aspect | @solidjs/router Adapter | @tanstack/solid-router Adapter |
|---|---|---|
| Router API | navigate(path) | router.navigate({ to }) |
| Setup Context | Must be inside Router context | Can wrap Router instance |
| State Source | Uses Solid hooks (useLocation) | Subscribes to router directly |
| Reacting | createEffect on location | router.subscribe callback |
Architecture
This package implements standard Play invariants:
INV-01: Actor Authority
State machine has final authority over all transitions. TanStack Router navigation triggers actor events (play.route), but the actor’s guards and transitions ultimately dictate if the view changes.
INV-02: Passive Infrastructure
Infrastructure reflects actor state. The router observes actor.currentRoute and updates the browser URL, never storing independent business state.
Cleanup Contract
The bridge implements an explicit dispose()/disconnect() method. PlayTanStackRouterProvider wires this automatically to Solid’s onCleanup hook to prevent memory leaks and duplicate bridge subscriptions in hot-reloading scenarios.
URLPattern Support
This package uses the URLPattern API for robust route pattern matching via @xmachines/play-router.
URLPattern is available natively on Node.js 24+ and modern browsers (Chrome 95+, Firefox 117+, Safari 16.4+). On older environments, load a polyfill before importing this package — see @xmachines/play-router installation for details.
Related Packages
- @xmachines/play-solid - SolidJS renderer
- @xmachines/play-router - Core router primitives
- @xmachines/play-solid-router - Native SolidJS Router equivalent
License
Copyright (c) 2016 Mikael Karon. All rights reserved.
This work is licensed under the terms of the MIT license.
For a copy, see https://opensource.org/licenses/MIT.