Skip to content

Instantly share code, notes, and snippets.

@aweber1
Last active May 26, 2023 13:26
Show Gist options
  • Save aweber1/85e0ce9ca6df74b4374d27cd0523c78a to your computer and use it in GitHub Desktop.
Save aweber1/85e0ce9ca6df74b4374d27cd0523c78a to your computer and use it in GitHub Desktop.
Next.js custom routes with regex + Server support + Client-side routing
const { rewrites } = require('./routeMatcher');
const nextConfig = {
experimental: {
rewrites: () => rewrites,
},
};
module.exports = nextConfig;
{
"dependencies": {
"path-to-regexp": "^6.1.0"
}
}
const MyPage = () => {
return <div></div>;
};
MyPage.getInitialProps = (nextContext) => {
const { pathname, query } = nextContext;
// `pathname` should be `index`, i.e. the `destination` property in our route matcher definitions.
console.log('pathname', pathname);
// `query.routeName` should be `/some-rad-page`, i.e. the `:routeName` parameter matched in our route matcher definitions.
console.log('query.routeName', query.routeName);
// `query.lang` may not be defined as it is "optional" in our route matcher definitions.
// However, if a route url is, for example, `/en/some-rad-page`, then `query.lang` would be `en`.
console.log('query.lang', query.lang);
}
// This file uses CommonJS because it is used by both server and client.
const { match: createMatcher } = require('path-to-regexp');
// `rewrites` are using by `next.config.experimental.rewrites` property and must be an
// array of objects with _only_ `source` and `destination` properties.
const rewrites = [
{
source: '/:lang([a-z]{2}-[A-Z]{2})/:routeName*',
destination: '/index',
},
{
source: '/:lang([a-z]{2})/:routeName*',
destination: '/index',
},
{
source: '/:routeName*',
destination: '/index',
},
];
// We create a new set of definitions mapped from `rewrites` and attach
// a `matcher` property that we can use for easier match evaluation in
// consuming code.
const routeMatcherDefinitions = rewrites.map((rewrite) => {
return { ...rewrite, matcher: createMatcher(rewrite.source) };
});
// matchRoute iterates `routeMatcherDefinitions` and attempts to match
// and parse the given `path`.
function matchRoute(path) {
let matchedRoute = null;
let matchedDefinition = null;
// Using a `for` loop allows us to break on the first match.
// Why not `for ... of`? because for...of loops get transpiled to something big and clunky.
// Why not Array.forEach or Array.reduce? because we can't break early.
for (let i = 0; i < routeMatcherDefinitions.length; i++) {
const routeDefinition = routeMatcherDefinitions[i];
// matcher returns false if no match made
matchedRoute = routeDefinition.matcher(path);
if (matchedRoute) {
matchedDefinition = routeDefinition;
break;
}
}
return { matchedRoute, matchedDefinition };
}
module.exports = {
matchRoute,
rewrites,
routeMatcherDefinitions,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment