@wearethreebears/next-named-routes
v0.1.0
Published
Type-safe named routes for Next.js.
Downloads
80
Readme
@wearethreebears/next-named-routes
Type-safe named routes generated from explicit names in Next.js App Router
pages. It does not require Next.js' typedRoutes option.
Install
pnpm add @wearethreebears/next-named-routesWrap the application's Next config:
// next.config.ts
import type { NextConfig } from "next";
import { withNamedRoutes } from "@wearethreebears/next-named-routes/plugin";
const nextConfig: NextConfig = {};
export default withNamedRoutes(nextConfig);Name pages
Add a non-exported literal route declaration to each page.tsx:
const route = "guest.login";
export default function LoginPage() {
return <main>Log in</main>;
}The declaration must not be exported. Next.js does not allow arbitrary named exports from App Router pages. If an unused-variable lint rule reports the marker, disable that rule for the declaration.
Names are explicit and do not have to mirror the folder structure. They must be unique dot-separated identifiers. Dynamic parameter names still come from the folder structure:
app/
(guest)/
login/page.tsx const route = "guest.login"
(auth)/
page.tsx const route = "auth"
subscriptions/
page.tsx const route = "auth.subscriptions"
[subscriptionId]/
page.tsx const route = "auth.subscriptions.id"
plans/[planId]/page.tsx const route = "auth.subscriptions.plans.id"Pages without a name fail generation by default. Set requireRouteName: false
to omit unnamed pages from the generated router.
Use named routes
The plugin generates routes.generated.ts next to the app directory. Import
its route function from application code:
import { route } from "./routes.generated";
route("guest.login");
route("auth");
route("auth.subscriptions");
route("auth.subscriptions.id", { subscriptionId });
route("auth.subscriptions.plans.id", { subscriptionId, planId });Route names are a generated string-literal union. Parameters are inferred from the corresponding dynamic segments, including catch-all and optional catch-all segments.
Query parameters and hashes
For static routes, pass config after the name:
route("guest.login", {
query: { returnTo: "/account", ref: ["email", "welcome"] },
hash: "form",
});For dynamic routes, path parameters and config are separate objects:
route(
"auth.subscriptions.plans.id",
{ subscriptionId, planId },
{
query: { trial: true },
hash: "pricing",
},
);Query values can be strings, numbers, booleans, null, undefined, or arrays
of those values. URLSearchParams and arrays of key-value pairs are accepted as
well. undefined values are omitted and array values become repeated query
parameters.
Plugin options
Pass options as the second argument to the config wrapper:
export default withNamedRoutes(nextConfig, {
appDir: "src/app",
output: "src/lib/routes.generated.ts",
extensions: ["tsx", "ts", "jsx", "js", "mdx"],
requireRouteName: true,
});The plugin scans app or src/app when the Next config loads and watches the
directory during development. It honors pageExtensions from the Next config
when extensions is not set explicitly.
Development
pnpm install
pnpm checkpnpm check runs strict TypeScript checks, compile-time API assertions, the
package build, and the route generator/runtime tests.
