Skip to content

Zegocover/next-route-resolver

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

next-route-resolver

Declarative route definition for Next.js

Allows you to define the routes for your Next.js website, to decouple them from the pages folder convention. The API is partly inspired by Django's URL configuration.

Motivation

  1. The default Next.js convention of mapping folder structures to URLs didn't fit our mental model of how websites should be structured.
  2. The facility for "masking" routes to bypass the pages convention is functional, but is lacking in the necessary abstractions to make it ergonomic.
  3. Existing solutions such as next-routes partially solve the problem, but use a chainable API that can be hard to compose and enable more advanced functionality.

Drawbacks

  1. Requires defining your own server (eg using Express.js) rather using the scripts Next.js gives you out of the box.
  2. Centralised routing needs to be in the core bundle, which could present a filesize issue for larger websites.

Installation

npm install next-route-resolver

or

yarn add next-route-resolver

Basic usage

Define some routes

import { compileRoutes } from "next-route-resolver";

const routes = compileRoutes([
  {
    path: "/",
    name: "homepage",
    page: "Homepage",
  },
  {
    path: "/posts/",
    name: "blog-index",
    page: "BlogIndex",
  },
  {
    path: "/posts/:slug/",
    name: "blog-post",
    page: "BlogPost",
  },
]);

Resolving URLs to a page and query

The returned object from the resolve function can be used to tell Next.js what page to render. You'll most likely use this as part of a request handler in your server code.

import { resolve } from "next-route-resolver";

import routes from "./routes";

resolve(routes, "/posts/my-first-blog-post/");
// { page: "./BlogPost", query: { slug: "my-first-blog-post"}}

Creating Link configuration

You'll also want to be able to create links to pages within your app. We've provided a function that will return necessary parameters for use with the Link component as well as the imperative API.

import { reverse } from "next-route-resolver";

import routes from "./routes";

reverse(routes, "blog-post", { slug: "my-first-blog-post" });
// { as: "/posts/my-first-blog-post/", href: { pathname: "/BlogPost", query: { slug: "my-first-blog-post" }}}

Includes

If your routes are getting too long and unweirdly, you can split them up.

import { compileRoutes, include } from "next-route-resolver";

const blogRoutes = [
  {
    path: "/posts/",
    name: "blog-index",
    page: "BlogIndex",
  },
  {
    path: "/posts/:slug/",
    name: "blog-post",
    page: "BlogPost",
  },
];

const routes = compileRoutes([
  include({ routes: blogRoutes }),
  {
    path: "/",
    name: "homepage",
    page: "Homepage",
  },
]);

You can also add a prefix to paths to make included routes more portable.

import { compileRoutes, include } from "next-route-resolver";

const blogRoutes = [
  {
    path: "/",
    name: "blog-index",
    page: "BlogIndex",
  },
  {
    path: "/:slug/",
    name: "blog-post",
    page: "BlogPost",
  },
];

const routes = compileRoutes([
  include({ routes: blogRoutes, prefix: "/posts/" }),
  {
    path: "/",
    name: "homepage",
    page: "Homepage",
  },
]);

Contextual includes

Let's say you want to make regional variants of your website. Perhaps under path prefixes such as /us/ and /es/. It's likely that you'll have some pages that are unique to each country, and some that are shared (but translated).

You could configure your routes as follows:

import { compileRoutes, include } from "next-route-resolver";

const usRoutes = [
  {
    path: "/contact-us/",
    name: "contact-us",
    page: "Contact",
  },
  {
    path: "/",
    name: "homepage",
    page: "HomepageUs",
  },
];

const esRoutes = [
  {
    path: "/contacto/",
    name: "contact-us",
    page: "Contact",
  },
  {
    path: "/",
    name: "homepage",
    page: "HomepageEs",
  },
];

const routes = compileRoutes([
  include({ routes: usRoutes, prefix: "/us/", params: { country: "us" } }),
  include({ routes: esRoutes, prefix: "/es/", params: { country: "es" } }),
]);

Now resolving and reversing are aware of the country parameter.

resolve(routes, "/us/");
// { page: "./HomepageUs", query: { country: "us"}}

reverse(routes, "homepage", { country: "us" });
// { as: "/us/", href: { pathname: "/HomepageUs", query: { country: "us" }}}

resolve(routes, "/es/contacto/");
// { page: "./Contact", query: { country: "es"}}

reverse(routes, "contact-us", { country: "es" });
// { as: "/es/contacto/", href: { pathname: "/Contact", query: { country: "es" }}}

Nesting parameters

Perhaps with the previous example, you want the US website to be the default rather than be scoped under a /us/ path. This is possible, because any parameters defined with include will override the same name parameters from higher up. So you could define your routes like this:

import { compileRoutes, include } from "next-route-resolver";

const usRoutes = [
  {
    path: "/contact-us/",
    name: "contact-us",
    page: "Contact",
  },
  {
    path: "/",
    name: "homepage",
    page: "HomepageUs",
  },
];

const esRoutes = [
  {
    path: "/contacto/",
    name: "contact-us",
    page: "Contact",
  },
  {
    path: "/",
    name: "homepage",
    page: "HomepageEs",
  },
];

const routes = compileRoutes(
  include({
    routes: [
      include({ routes: usRoutes }),
      include({ routes: esRoutes, prefix: "/es/", params: { country: "es" } }),
    ],
    params: { country: "us" },
  }),
);

You'd now be able to resolve and reverse like this:

resolve(routes, "/");
// { page: "./HomepageUs", query: { country: "us"}}

reverse(routes, "homepage", { country: "us" });
// { as: "/", href: { pathname: "/HomepageUs", query: { country: "us" }}}

resolve(routes, "/es/contacto/");
// { page: "./Contact", query: { country: "es"}}

reverse(routes, "contact-us", { country: "es" });
// { as: "/es/contacto/", href: { pathname: "/Contact", query: { country: "es" }}}

About

Declarative route definition for Next.js

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published