Solid Router

repository·main·Indexed 23 days ago

https://github.com/solidjs/solid-router

A universal routing library for SolidJS supporting nested routes, dynamic parameters, and advanced data preloading to prevent loading waterfalls. It includes a query API for data fetching and caching, createAsync and createAsyncStore for reactive data handling, and an action system for data mutations with support for optimistic updates via useSubmission.

Tokens
15K
Snippets
41
Records
82
Agent score
79%

What's inside @solidjs/router

  1. Use dynamic route parameters

    main

    Use a colon (:) to define a flexible segment in a path. This segment acts as a parameter that can be accessed within the component using the useParams hook.

    <Route path="/users/:id" component={User} />

    Note on Animation/Transitions: Routes sharing the same path match are treated as the same route. To force a re-render when parameters change, wrap your component in a keyed <Show>:

    <Show when={params.something} keyed>
      <MyComponent />
    </Show>
    import { lazy } from "solid-js";
    import { render } from "solid-js/web";
    import { Router, Route } from "@solidjs/router";
    
    const Users = lazy(() => import("./pages/Users"));
    const User = lazy(() => import("./pages/User"));
    const Home = lazy(() => import("./pages/Home"));
    
    render(
      () => (
        <Router>
          <Route path="/users" component={Users} />
          <Route path="/users/:id" component={User} />
          <Route path="/" component={Home} />
        </Router>
      ),
      document.getElementById("app")
    );
  2. How nested routes work in Solid Router

    main

    Nested routes allow you to define a hierarchy of components that render based on the URL path.

    Key Behaviors:

    • Leaf Nodes vs. Parents: Only leaf nodes (the innermost Route components) are automatically assigned a route. If you want a parent component to render at its own specific path, you must define it explicitly or use a child route with a path of /.
    • Rendering via props.children: When nesting routes, you can use props.children within a parent component to specify where the child route's component should be rendered.
    • Indefinite Nesting: You can nest routes as deeply as needed. Each level of nesting will wrap the child content in the parent's component structure.
    // Pattern 1: Explicitly defining parent and child routes
    <Route path="/users" component={Users} />
    <Route path="/users/:id" component={User} />
    
    // Pattern 2: Using props.children to render nested content
    function PageWrapper(props) {
      return (
        <div>
          <h1> We love our users! </h1>
          {props.children}
          <A href="/">Back Home</A>
        </div>
      );
    }
    
    <Route path="/users" component={PageWrapper}>
      <Route path="/" component={Users} />
      <Route path="/:id" component={User} />
    </Route>;
  3. Understanding the new Preload mechanism vs old `data` functions

    main

    The previous data functions and useRouteData hook have been replaced by a built-in preload mechanism.

    Benefits of Preloading:

    • Link Hover Preloads: Preload functions can be executed on hover without side effects on reactivity.
    • Caching Control: Supports deduping and query APIs for better cache management.
    • Improved TypeScript Support: Resolves type issues when accessing route data within components without needing typeof checks.

    Reproducing the old pattern (Manual Context Injection)

    If you prefer the old pattern of injecting data via Context, you can disable preloads at the Router level and manually handle data fetching and injection:

    import { lazy } from "solid-js";
    import { Route, Router } from "@solidjs/router";
    
    const User = lazy(() => import("./pages/users/[id].js"));
    
    // preload function
    function preloadUser({ params, location }) {
      const [user] = createResource(() => params.id, fetchUser);
      return user;
    }
    
    // Pass it in the route definition
    <Router preload={false}>
      <Route path="/users/:id" component={User} preload={preloadUser} />
    </Router>;

    Then, in your component, wrap the content with a Context Provider:

    function User(props) {
      return (
        <UserContext.Provider value={props.data}>
          {/* my component content  */}
        </UserContext.Provider>
      );
    }
    
    // Somewhere else in the tree
    function UserDetails() {
      const user = useContext(UserContext);
      // render stuff
    }
  4. Migrating from `element` prop to `component` prop in `Route`

    main
    The element prop has been removed from the Route component. Previously, element allowed passing a component directly, but this caused confusion and edge cases regarding how components were rendered relative to the Outlet. You should now use the component prop to define your route components.
  5. Use wildcard routes

    main

    The * token matches any arbitrary end of a path. You can name the wildcard to expose the matched segment as a parameter.

    // Matches /foo, /foo/, /foo/a/, /foo/a/b/c
    <Route path="foo/*" component={Foo} />
    
    // Matches /foo/bar and exposes 'any' as a parameter
    <Route path="foo/*any" component={Foo} />

    Note: The wildcard token must be the last part of the path (e.g., foo/*any/bar is invalid).

    <Route path="foo/*" component={Foo} />
    <Route path="foo/*any" component={Foo} />
  6. Define multiple paths for a single route

    main

    You can pass an array of paths to the path prop of a <Route />. This allows the same component to be mounted for multiple locations without causing a re-render when switching between them.

    // Navigating from /login to /register will not cause the Login component to re-render
    <Route path={["login", "register"]} component={Login} />
    <Route path={["login", "register"]} component={Login} />
  7. Provide a root level layout with the `root` prop

    main

    The Router component accepts a root prop. The component passed to root acts as a top-level layout that remains mounted during navigation. This is the ideal place for global navigation, headers, footers, or Context Providers. The route components are rendered as {props.children} within this root component.

    import { render } from "solid-js/web";
    import { Router, Route } from "@solidjs/router";
    
    import Home from "./pages/Home";
    import Users from "./pages/Users";
    
    const App = (props) => (
      <>
        <h1>My Site with lots of pages</h1>
        {props.children}
      </>
    );
    
    render(
      () => (
        <Router root={App}>
          <Route path="/users" component={Users} />
          <Route path="/" component={Home} />
        </Router>
      ),
      document.getElementById("app")
    );
  8. Lazy-load route components

    main

    To optimize performance, you can use Solid's lazy function to load route components only when they are navigated to.

    import { lazy } from "solid-js";
    import { render } from "solid-js/web";
    import { Router, Route } from "@solidjs/router";
    
    const Users = lazy(() => import("./pages/Users"));
    const Home = lazy(() => import("./pages/Home"));
    
    const App = (props) => (
      <>
        <h1>My Site with lots of pages</h1>
        {props.children}
      </>
    );
    
    render(
      () => (
        <Router root={App}>
          <Route path="/users" component={Users} />
          <Route path="/" component={Home} />
        </Router>
      ),
      document.getElementById("app")
    );
  9. Configure redirects for SPAs in deployed environments

    main

    When deploying a Single Page Application (SPA) that uses client-side routing without Server-Side Rendering (SSR), you must configure your hosting provider to redirect all requests to your index.html. This prevents 404 errors when a user refreshes the page on a route that isn't a physical file on the server.

    Netlify Configuration

    Create a _redirects file in your deployment directory with the following content:

    /*   /index.html   200

    Vercel Configuration

    Add a rewrites section to your vercel.json file:

    {
      "rewrites": [
        {
          "source": "/(.*)",
          "destination": "/index.html"
        }
      ]
    }
  10. Use optional parameters in routes

    main

    To make a parameter optional, append a question mark (?) to the parameter name.

    // Matches /stories and /stories/123, but not /stories/123/comments
    <Route path="/stories/:id?" component={Stories} />
    <Route path="/stories/:id?" component={Stories} />
  11. Set up the Router component

    main

    After installation, start your application by rendering the Router component. This component matches the current URL to display the appropriate page.

    import { render } from "solid-js/web";
    import { Router } from "@solidjs/router";
    
    render(() => <Router />, document.getElementById("app"));