npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xmachines/play-solid-router

v2.1.1

Published

SolidJS Router adapter for XMachines Universal Player Architecture

Readme

@xmachines/play-solid-router

SolidJS Router adapter for the XMachines Universal Player Architecture. It keeps the state machine routes of a PlayerActor and the browser URL in step, in both directions, through @solidjs/router.

License: MIT Version

Installation

pnpm add @xmachines/play-solid-router

Peer dependencies. Install them separately:

pnpm add solid-js @solidjs/router xstate
  • solid-js ^1.8.0
  • @solidjs/router ^0.16.1
  • xstate ^5.31.0

Quick Start

import { Router, Route, useNavigate, useLocation, useParams } from "@solidjs/router";
import { onCleanup, type ParentComponent } from "solid-js";
import { PlayRouterProvider, createRouteMap } from "@xmachines/play-solid-router";
import { definePlayer } from "@xmachines/play-xstate";
import { myMachine } from "./machine.js";

const actor = definePlayer({ machine: myMachine })();
actor.start();

const routeMap = createRouteMap(myMachine);

// Minimal app shell stub — a real app renders PlayUIProvider + PlayRenderer from
// @xmachines/play-solid here (see the workspace-only @xmachines/play-solid-demo Shell)
const MyApp = (props: { actor: typeof actor }) => <main />;

const Layout: ParentComponent = () => {
	const navigate = useNavigate();
	const location = useLocation();
	const params = useParams();

	onCleanup(() => actor.stop());

	return (
		<PlayRouterProvider
			actor={actor}
			routeMap={routeMap}
			router={{ navigate, location, params }}
			renderer={(a, router) => <MyApp actor={a} />}
		/>
	);
};

export default function App() {
	return <Router root={Layout}>{/* one <Route> per routable state */}</Router>;
}

API Summary

PlayRouterProvider

This SolidJS component connects a PlayerActor to Solid Router. It creates and connects a SolidRouterBridge on mount. It disconnects the bridge with onCleanup on unmount.

interface PlayRouterProviderProps<TActor extends PlayActor> {
	/** The actor to sync with Solid Router. */
	actor: TActor;
	/** Bidirectional route map for state ID ↔ URL path lookups. */
	routeMap: RouteMap;
	/**
	 * The three Solid Router hook results that drive bidirectional sync.
	 * Must be obtained via useNavigate(), useLocation(), and useParams()
	 * inside a router context.
	 */
	router: SolidRouterHooks;
	/** Render callback — receives the concrete actor type and router hooks. */
	renderer: (actor: TActor, router: SolidRouterHooks) => JSX.Element;
}

SolidRouterBridge

The low-level class for a manual integration. It extends RouterBridgeBase from @xmachines/play-router. It uses the Solid createEffect to send each router change to the actor.

Important: call connect() inside a Solid reactive owner: a component, or createRoot. The bridge does not clean up by itself. Call disconnect() or dispose() yourself, usually in onCleanup().

import { useNavigate, useLocation, useParams } from "@solidjs/router";
import { onCleanup } from "solid-js";
import { SolidRouterBridge, RouteMap } from "@xmachines/play-solid-router";

// actor: your started player (see the Quick Start above)

function App() {
	const navigate = useNavigate();
	const location = useLocation();
	const params = useParams();

	const routeMap = new RouteMap([
		{ stateId: "#home", path: "/" },
		{ stateId: "#profile", path: "/profile/:userId" },
	]);

	const bridge = new SolidRouterBridge(navigate, location, params, actor, routeMap);
	bridge.connect();
	onCleanup(() => bridge.disconnect());

	return <div>...</div>;
}

createRouteMap(machine)

This factory builds a RouteMap directly from an XState machine definition. It comes from @xmachines/play-router.

import { createRouteMap } from "@xmachines/play-solid-router";

const routeMap = createRouteMap(myMachine);

RouteMap / RouteMapping

The bidirectional map between the state IDs and the URL paths. It comes from @xmachines/play-router.

import { RouteMap } from "@xmachines/play-solid-router";

const routeMap = new RouteMap([
	{ stateId: "#home", path: "/" },
	{ stateId: "#profile", path: "/profile/:userId" },
	{ stateId: "#settings", path: "/settings/:section?" },
]);

Types

| Export | Description | | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | PlayActor | AbstractActor & Routable & Viewable — the canonical actor shape from @xmachines/play-router. PlayRouterProvider requires it, because it renders the current view spec and also keeps the routes in step. | | RoutableActor | Deprecated alias for PlayActor. Use PlayActor from @xmachines/play-router in new code. | | AbstractActor | It comes from @xmachines/play-actor. Use it for the type of a renderer callback. | | SolidRouterHooks | Shape of the router prop: { navigate, location, params } | | PlayRouterProviderProps | Full props interface for PlayRouterProvider | | PlayRouteEvent | The event type that the bridge sends to the actor on a URL change (play.route) | | RouterBridge | The interface that SolidRouterBridge implements | | RouteMapOptions | The options object for the RouteMap constructor. It comes from @xmachines/play-router. |

Usage Patterns

Protected Routes and Guards

The auth guards are inside the state machine only. Unauthorized content therefore never appears, not even for a moment:

const machineConfig = {
	states: {
		dashboard: {
			meta: { route: "/dashboard" },
			always: {
				guard: ({ context }) => !context.isAuthenticated,
				target: "login",
			},
		},
	},
};

A user navigates to /dashboard, and the user is not authenticated:

  1. Solid Router updates the URL.
  2. The bridge receives the change and sends play.route to the actor.
  3. The actor evaluates the guard. The guard refuses the transition, and the actor moves to login.
  4. The bridge reads the new actor route (/login) from the TC39 Signal.
  5. The bridge calls navigate("/login").

Dynamic Routes with Parameters

const routeMap = new RouteMap([
	{ stateId: "#post", path: "/users/:userId/posts/:postId" },
	{ stateId: "#settings", path: "/settings/:section?" },
]);

// Params are extracted from Solid's useParams() and forwarded in the play.route event:
// { type: "play.route", to: "#post", params: { userId: "123", postId: "456" }, query: {} }

The bridge reads the path parameters from the reactive useParams() proxy of Solid. A parameterized route therefore does not need the URLPattern polyfill.

Testing

Run tests for this package in isolation:

# From the monorepo root
pnpm --filter @xmachines/play-solid-router test

# Or from this package directory
pnpm test

Browser tests (test/browser/**/*.browser.test.ts) run in real Chromium through Playwright. They cover the asynchronous sequences that jsdom cannot reproduce:

pnpm exec vitest --config vitest.browser.config.ts --project play-solid-router-browser

Coverage thresholds: 80% lines, functions, branches, and statements.

Related Packages

Learn More

License

MIT — see LICENSE.