I'm using this approach to have centralized route config in Next.js. I don't have the fully automated solution. Right now I'm writing route configs and links manually to figure out if it works and what are the drawbacks.
I believe the automation is the last step to make it completely seamless.
This is the only file that needs to be wrote manually.
//route.config.ts
const routeConfig = {
// url: filename inside `pages`
'/about': '/about',
'/article/:id': '/article',
}
Only imported Links/routes are included in bundle, everything else is stripped down using tree-shaking.
Each route is transformed into routeConfig
and custom Link
component. routeConfig
can be used for imperative routing (e.g. Router.push
), while custom Link
is
used in JSX for declarative routing.
// links.ts
// generated from route.config.ts
export const about = makeRouteConfig("/about")
export const AboutLink = makeLink(about)
export const article = makeRouteConfig("/article/:id", "/article")
export const ArticleLink = makeLink(article)
As a bonus, links can be typechecked:
// links.d.ts
interface CustomLinkProps<Params> extends LinkProps {
params: Params
}
interface ArticleLinkParams {}
declare const ArticleLink = React.ElementType<CustomLinkProps<AboutLinkParams>>
{
"routes": [
{ "src": "/about", "dest": "/about.js" },
{ "src": "/article/(?<id>[^/]*)", "dest": "/article.js?id=$id" },
]
}
// pages/index.ts
import { AboutLink, ArticleLink } from "../links"
export default () => {
<nav>
<AboutLink>About</AboutLink>
<ArticleLink params={{id: "42"}}>Answering the ultimate question</ArticleLink>
</nav>
}
✅ this can be build on top of current Next.js API
✅ centralized route config with minimal footprint in bundle
✅ global server handler is used only for development. Pages in production can be served in serverless fashion.
🚫 requires extra compile step to prepare now.json
and links.ts
Current implementation of helpers which I use in previous examples. Not important for the concept.
Little helper to create routeConfig object.
export interface RouteConfig {
path: string
as: string
}
export function makeRouteConfig(as: string, path?: string): RouteConfig {
if (process.env.NODE_ENV !== "production") {
if (!as.startsWith("/")) {
throw Error(`path ${as} should start with /`)
}
}
return {
as,
path: path != null ? path : as
}
}
Factory to make custom Link
with href
and as
attributes
filled automatically based on routeConfig
object
import * as React from "react"
import Link, { LinkProps } from "next/link"
import { reverse, RouteConfig } from "accent/core/routes"
export interface RouteLinkProps extends LinkProps {
params?: { [key: string]: string }
}
// Create customized Link component, which fills `href` and `as` props based on routeConfig
export const makeLink = (routeConfig: RouteConfig) => ({
params,
children,
...linkProps
}: RouteLinkProps) => {
const href = {
pathname: routeConfig.path,
query: params
}
const as = params == null ? routeConfig.as : reverse(routeConfig, params)
return (
<Link href={href} as={as} {...linkProps}>
{children}
</Link>
)
}