@mertcreates/eslint-plugin-next-pages-router
v1.0.0
Published
ESLint rules to validate Next.js Pages Router route comparisons and navigation targets against your pages manifest.
Maintainers
Readme
eslint-plugin-next-pages-router
ESLint rules to catch invalid Pages Router route comparisons and navigation
calls. It validates hardcoded route strings against your pages/ manifest and
helps prevent typos or mismatched patterns.
Pages Router only. App Router (app/) is out of scope.
If you use the App Router, consider Next.js built-in typed routes instead.
Contents
Features
- Validates route comparisons (
===,.includes(),switch) - Checks
router.push/router.replacetargets - Distinguishes patterns (
/posts/[id]) from concrete paths (/posts/123) - Handles query strings, hashes, trailing slashes,
basePath, andi18n.locales - Provides ESLint suggestions (quick fixes in supporting editors)
Install
npm i -D @mertcreates/eslint-plugin-next-pages-router
# or
yarn add -D @mertcreates/eslint-plugin-next-pages-router
# or
pnpm add -D @mertcreates/eslint-plugin-next-pages-router
# or
bun add -D @mertcreates/eslint-plugin-next-pages-routerUsage (eslintrc)
{
"extends": ["plugin:@mertcreates/next-pages-router/recommended"]
}Usage (flat config)
const nextRouting = require('@mertcreates/eslint-plugin-next-pages-router');
module.exports = [
nextRouting.configs['flat/recommended'],
];Rules
The recommended config enables both rules.
@mertcreates/next-pages-router/no-invalid-route-compare
Validates that:
router.routeandrouter.pathnameare compared against route patterns that exist inpages/(e.g.'/posts/[id]').router.asPathis compared against concrete URLs (e.g.'/posts/123') and matches an existing pages route.- Query strings or hashes (
?/#) are only used withasPath.
Suggestions are provided as ESLint suggestions when
suggestClosestRoute is enabled (default: on in VS Code, off in CLI).
Incorrect:
router.route === '/posts/123'
router.asPath === '/posts/[id]'Correct:
router.route === '/posts/[id]'
router.asPath === '/posts/123?sort=asc'@mertcreates/next-pages-router/no-invalid-router-navigation
Validates router.push and router.replace targets:
- String URLs must be concrete and match a pages route.
- URL objects may use a route pattern in
pathnamewithquery, or a concretepathnamethat matches a pages route. - Passing a pattern string is only valid when an
asURL is provided. asmust be a concrete URL (no route patterns).
When preferUrlObject is enabled (default), string url + string as
usage is reported as legacy. Use a UrlObject instead.
Incorrect:
router.push('/unknown')
router.push('/posts/[id]')Correct:
router.push('/posts/123')
router.push({ pathname: '/posts/[id]', query: { id: '123' } })Options
Options below are optional and provided as the first rule option. Options are set per rule, and some only apply to the compare rule.
{
"rules": {
"@mertcreates/next-pages-router/no-invalid-route-compare": [
"warn",
{
"pagesDir": "pages",
"readNextConfig": true,
"nextConfigPath": "./apps/web/next.config.js",
"basePath": "/docs",
"locales": ["en", "tr"],
"routerObjects": ["router", "Router", "props.router"],
"routeProperties": ["route", "pathname"],
"checkEquality": true,
"checkIncludes": true,
"checkSwitch": true,
"warnOnUnknownPaths": true,
"suggestClosestRoute": true,
"skipIfPagesDirMissing": true
}
],
"@mertcreates/next-pages-router/no-invalid-router-navigation": [
"warn",
{
"pagesDir": "pages",
"readNextConfig": true,
"nextConfigPath": "./apps/web/next.config.js",
"basePath": "/docs",
"locales": ["en", "tr"],
"routerObjects": ["router", "Router", "props.router"],
"warnOnUnknownPaths": true,
"suggestClosestRoute": true,
"preferUrlObject": true,
"skipIfPagesDirMissing": true
}
]
}
}Option reference:
| Option | Type | Default | Applies to | Description |
| --- | --- | --- | --- | --- |
| pagesDir | string | "pages" | both | Directory for Next.js pages. |
| readNextConfig | boolean | false | both | If true, reads basePath and i18n.locales from next.config.*. |
| nextConfigPath | string | "" | both | Optional path to Next config when readNextConfig is enabled. |
| basePath | string | "" | both | Overrides Next config. |
| locales | string[] | [] | both | Overrides Next config. |
| routerObjects | string[] | ["router", "Router"] | both | Allow-list of router object identifiers or member paths (e.g. "router", "props.router"). |
| routeProperties | string[] | ["route","pathname"] | compare | Router fields treated as route patterns. |
| checkEquality | boolean | true | compare | Enable ===/== comparisons. |
| checkIncludes | boolean | true | compare | Enable includes(...) checks. |
| checkSwitch | boolean | true | compare | Enable switch (...) checks. |
| warnOnUnknownPaths | boolean | true | both | Warn when asPath or navigation targets do not match any known pages route. |
| suggestClosestRoute | boolean | true in VS Code, false in CLI | both | Adds "Did you mean" suggestions. When set, it overrides the default behavior. |
| preferUrlObject | boolean | true | navigation | Report legacy router.push(pattern, as) usage and prefer UrlObject with pathname + query. |
| skipIfPagesDirMissing | boolean | true | both | Skip checks when the pagesDir doesn't exist (useful in monorepos or non-Next builds). |
Compatibility
- Node:
>=16 - ESLint:
8or9 - Next.js:
>=10(Pages Router)
Benchmarks
Benchmarks here measure rule overhead (not total ESLint time).
Latest run (mixed mode, 12k statements across 80 files, 5-run average):
- Real project pages (46 routes): ~2–3 ms per run
- Synthetic stress (6000 routes): ~2–3 ms per run
See benchmark details in BENCHMARKS.md.
License
MIT.
