Skip to content

$route

import { $route } from "dreamkit";

With the route builder you can define and use search and path parameters with typing and validation.

You can also consume functions created with $api to securely exchange data with the server.

When you create a route in development mode, metadata will be automatically generated and you will have strong typing to access routes and their parameters in the Link component, the routePath function, and the RequestUrl class.

Also, in development mode, if you use filesystem routing with path params (ex: src/routes/users/[id].tsx) and you do not define the scheme for these params, a TypeScript warning will be raised.


Definition

import type { Component } from "solid-js";
const $route: {
title(value: string): typeof $route;
params(value: object): typeof $route;
onParamsError(input: { value: any } | { redirect: string }): typeof $route;
path(value: string | ((params: object) => string)): typeof $route;
api(value: Record<string, Function>): typeof $route;
preload(
value: (data: { intent: string; params: object }) => any,
): typeof $route;
create(
component: (utils: {
params: object;
setParams: (value: object) => void;
api: Record<string, Function>;
}) => any,
): Component;
};

title

Set a title to the page using the Title component of @solidjs/meta.

params

Define the parameters schema of the route.

By default, all parameters are used as search parameters, but you can use them in the path using the path option.

$route.params({ name: s.string() });

onParamsError

If an error occurs when validating parameters, you can choose to reset the parameters to a predefined valid value:

$route.params({ name: s.string() }).onParamsError({ value: "" });

Or redirect to another page:

$route.params({ name: s.string() }).onParamsError({ redirect: "/error" });

path

You can define the route path in two ways, using a fixed route without params (ex: /user?id=1):

$route.params({ id: s.number }).path("/user");

Or by injecting part of the previously defined parameter scheme (ex: /user/1):

$route.params({ id: s.number }).path((params) => `/user/${params.id}`);

If you are creating the route under the file system (ex: /src/routes/users.tsx), you don’t need to define the path.

api

Injects API functions created with $api so they can be used in the component.

const fetchPid = $api.create(() => process.pid);
$route.api({ fetchPid }).create(({ api }) => {
api.fetchPid().then((pid) => console.log(pid));
});

preload

Preloads data before accessing the route (default when hovering over the link).

const fetchPid = $api.create(() => process.pid);
$route
.api({ fetchPid })
.preload(({ api }) => api.fetchPid())
.create(({ data }) => {
data.then((pid) => console.log(pid));
});

create

Creates the route component with all the options previously entered.

The component receives an argument with the following properties:

  • params: the parameters of the route configured with the params option.
  • setParams: a function to set the search params of the route with type safe.
  • api: the API functions injected with the api option.
  • data: the data preloaded in the preload option.
$route.create(({ params, setParams, api, data }) => {});

FAQ

Why inject API functions instead of using them directly?

By injecting API functions into the route you can get a tree of which APIs each route uses and thus be able to manage access permissions later.

If the API function you create consumes dependencies it cannot be called directly without generating an error in TypeScript:

const exec = $api.self({ Db }).create(() => {});
exec(); // TypeScript error
exec.bind({ db: new Db() })(); // OK

Examples

Path param

import { $route, Link, s } from "dreamkit";
export const homeRoute = $route.path("/").create(() => (
<>
<Link href="/user/:id" params={{ id: 1 }}>
user 1
</Link>
<br />
<Link href="/user/:id" params={{ id: 2 }}>
user 2
</Link>
</>
));
export const userRoute = $route
.params({ id: s.number() })
.path((params) => `/user/${params.id}`)
.create(({ params }) => <>id: {params.id}</>);

Optional path params

import { $route, Link, s } from "dreamkit";
export const homeRoute = $route.path("/").create(() => (
<>
<Link href="/section">link without params</Link>
<br />
<Link href="/section" params={{ name: "test" }}>
link with params
</Link>
</>
));
export const sectionRoute = $route
.path("/section")
.params({ name: s.string().optional() })
.create(({ params }) => <>name: {params.name}</>);

Preload with cache

import { $api, $route, Link, s } from "dreamkit";
import { createResource } from "solid-js";
const fetchData = $api
.cache("users")
.params({ id: s.number() })
.create(({ id }) => {
console.log("Fetching data", id);
return Date.now();
});
export const userRoute = $route
.params(fetchData.params)
.path(({ id }) => `/user/${id}`)
.api({ fetchData })
.preload(({ api, params }) => api.fetchData(params))
.create(({ data }) => {
const [res] = createResource(() => data);
return <>{res()}</>;
});
export default $route.path("/").create(() => (
<>
<Link href="/user/:id" params={{ id: 1 }}>
user 1
</Link>
<br />
<Link href="/user/:id" params={{ id: 2 }}>
user 2
</Link>
</>
));

Synced search params

import { Input, $route, s } from "dreamkit";
export default $route
.path("/")
.params(
s
.object({
name: s.string(),
country: s.object({
code: s.string(),
}),
})
.deepPartial(),
)
.create(({ params, setParams }) => {
return (
<>
<i>Look at the address bar of your browser.</i>
<p>
<Input
placeholder="name"
value={params.name ?? ""}
onChange={(name) =>
setParams({ ...params, name: name || undefined })
}
/>
</p>
<p>
<Input
placeholder="country code"
value={params.country?.code ?? ""}
onChange={(code) =>
setParams({
...params,
country: { ...params.country, code: code || undefined },
})
}
/>
</p>
</>
);
});