build
build(template, params, query?, options?): string
Standalone path resolver — for building a URL without going through defineRoutes, or for resolving a raw template string (rather than a route from a PATHS tree).
import { build } from "react-routes-forge";
build("/users/:id/posts/:postId", { id: 1, postId: 42 });
// → '/users/1/posts/42'
// Append a query string to any path — including static ones
build("/users", {}, { sort: "asc" });
// → '/users?sort=asc'
// Strict mode — throw instead of warn when a param is missing
build("/users/:id", {}, undefined, { strict: true });
// ✗ throws RangeError
// Hash fragment — appended after the query string
build("/users/:id", { id: 42 }, { tab: "info" }, { hash: "details" });
// → '/users/42?tab=info#details'
// Locale prefix — pre-pends the path with a locale segment
build("/users/:id", { id: 42 }, undefined, { locale: "en-US" });
// → '/en-US/users/42'
// Param values are URL-encoded by default
build("/search/:query", { query: "a/b" });
// → '/search/a%2Fb'
// Pass { encode: false } if a value is already encoded
build("/search/:query", { query: "a%2Fb" }, undefined, { encode: false });
// → '/search/a%2Fb'
// Splat segments capture a path-like remainder, preserving `/` separators
build("/files/*", { "*": "reports/2026/q1" });
// → '/files/reports/2026/q1'See Splat Segments for details.
buildRelative
buildRelative has the exact same signature as build(), but it returns a relative path by stripping the leading slash (returning "." if the path is empty). It is available as a standalone function buildRelative() and as .buildRelative() on route values.
import { defineRoutes, buildRelative } from "react-routes-forge";
const PATHS = defineRoutes({
USERS: {
EDIT: "/users/:id/edit"
}
} as const);
PATHS.USERS.EDIT.buildRelative({ id: 42 });
// → 'users/42/edit'
buildRelative("/users/:id", { id: 42 });
// → 'users/42'Query String Serialization
Every path-resolving function — .build(), build(), and useResolvedPath() — accepts an optional query object as its second-to-last argument.
navigate(
PATHS.USERS.DETAILS.build({ id: 42 }, { tab: "billing", sort: "asc" }),
);
// → '/users/42?tab=billing&sort=asc'Array values are serialized as repeated keys:
build("/search", {}, { tags: ["admin", "moderator"] });
// → '/search?tags=admin&tags=moderator'Boolean values serialize to "true"/"false":
build("/search", {}, { active: true, draft: false });
// → '/search?active=true&draft=false'null and undefined values are dropped, so you can pass optional filters without conditionally building the object:
build("/users", {}, { sort: "asc", filter: undefined });
// → '/users?sort=asc'Static routes have a fluent .build(query?, options?) — there are no params to interpolate, but query strings and hashes still work:
PATHS.USERS.ROOT.build({ sort: "asc", page: 2 });
// → '/users?sort=asc&page=2'
// You can also pass options to static routes
PATHS.USERS.ROOT.build({}, { hash: "top" });
// → '/users#top'Note that for static routes, .build() is completely optional if you don't need to append query parameters or a hash. You can pass PATHS.USERS.ROOT directly to <Link to> or navigate().
Hash Fragments
URL hash fragments (#section) are supported via the hash option in every path-resolving function — .build(), build(), and useResolvedPath(). The hash is appended after the query string, if any.
// Via fluent .build() on a dynamic route
PATHS.USERS.DETAILS.build({ id: 42 }, undefined, { hash: "profile" });
// → '/users/42#profile'
// With query + hash
PATHS.USERS.DETAILS.build({ id: 42 }, { tab: "info" }, { hash: "details" });
// → '/users/42?tab=info#details'
// Via standalone build()
build("/page", {}, undefined, { hash: "section" });
// → '/page#section'The leading # is added automatically — pass just the fragment name (e.g. "details", not "#details").
URL Encoding Edge Cases
By default, all parameter values are URL-encoded using encodeURIComponent.
build("/search/:query", { query: "foo/bar?baz" });
// → '/search/foo%2Fbar%3Fbaz'However, there are a few exceptions and options to be aware of:
- Opting out of encoding: Pass
{ encode: false }if your value is already encoded or if you specifically need raw characters to pass through (use with caution). - Splat segments (
*): Splat segments are treated as path-like. Forward slashes (/) are preserved, but other special characters (like?and#) are still encoded to prevent them from breaking the URL structure.tsbuild("/files/*", { "*": "folder/file name?" }); // → '/files/folder/file%20name%3F' - Double encoding: React Router v7's
generatePath(whichreact-routes-forgedoes not use under the hood) applies its own URL encoding. Becausereact-routes-forgebuilds the fully encoded string itself, you don't need to worry about double-encoding when passing the result to React Router components like<Link>.