nico.fyi
    Published on

    Code based routing in Next.js

    Authors

    Some people seem to hate the file-based routing in Next.js. They prefer to use code-based routing instead. And with Remix, a.k.a, React Router 7 releasing a new config based routing, many people seem to be excited about it.

    But many don't seem to know that it has been possible to do code-based routing in Next.js. You just have to use the optional catch-all segments of the dynamic routes and library like path-to-regexp.

    First, create a file in app/[[...paths]]/page.tsx:

    app/[[...paths]]/page.tsx
    import UserIdPage from "@/page-components/user-id";
    import UsersPage from "@/page-components/users";
    import { match } from "path-to-regexp";
    
    // define your routes here
    const routes = [
      { matcher: match("/users/:userId"), component: UserIdPage },
      { matcher: match("/users"), component: UsersPage },
    ] as const;
    
    export default function CatchAll({
      params,
    }: {
      params?: { paths?: string[] };
    }) {
      if (!params?.paths) return <div>Main page</div>;
    
      let route:
        | {
            component: React.ElementType;
            params: Record<string, unknown> | undefined;
            path: string[];
          }
        | undefined;
    
      for (const r of routes) {
        const matched = r.matcher(`/${params.paths.join("/")}`);
        if (matched) {
          route = {
            component: r.component,
            params: matched.params,
            path: params.paths,
          };
          break;
        }
      }
    
      if (route) {
        return <route.component {...route.params} />;
      }
    
      return <div>CatchAll: {params.paths.join("/")}</div>;
    }
    

    What's cool about this is that you can still use the file-based routing in the app directory if you want. So if you have a file in app/admins/page.tsx, you can still use it as /admins in the URL.

    Furthermore, you don't actually need to use this code-based routing approach from the root URL. You can use it from some other nested paths like app/dashboard/[[...paths]]/page.tsx and it will work as expected.


    By the way, I'm making a book about Pull Requests Best Practices. Check it out!