UNPKG

eslint-plugin-nextjs-app-router

Version:

ESLint plugin for Next.js App Router to enforce best practices and catch common mistakes

289 lines (207 loc) • 6.77 kB
# eslint-plugin-nextjs-app-router [![npm version](https://img.shields.io/npm/v/eslint-plugin-nextjs-app-router.svg)](https://www.npmjs.com/package/eslint-plugin-nextjs-app-router) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) ESLint plugin for Next.js App Router to enforce best practices and catch common mistakes with Server Components, Client Components, and Server Actions. ## Features - šŸ” Detect async components in client components - āš›ļø Enforce `"use client"` directive when using React hooks - šŸ”„ Ensure Server Actions return Promises - šŸŽÆ Type-safe and easy to integrate ## Installation ```bash bun add -d eslint-plugin-nextjs-app-router ``` or with npm: ```bash npm install --save-dev eslint-plugin-nextjs-app-router ``` or with yarn: ```bash yarn add -D eslint-plugin-nextjs-app-router ``` or with pnpm: ```bash pnpm add -D eslint-plugin-nextjs-app-router ``` ## Usage Add `nextjs-app-router` to the plugins section of your `.eslintrc` configuration file: ```json { "plugins": ["nextjs-app-router"], "rules": { "nextjs-app-router/no-async-client-component": "error", "nextjs-app-router/use-client-must-exist-with-hooks": "error", "nextjs-app-router/use-server-must-return-promise": "error" } } ``` Or use the recommended configuration: ```json { "extends": ["plugin:nextjs-app-router/recommended"] } ``` ## Rules ### `no-async-client-component` Ensures that files with `"use client"` directive do not export async components. āŒ **Incorrect:** ```tsx "use client"; export async function MyComponent() { const data = await fetch("/api/data"); return <div>{data}</div>; } ``` āœ… **Correct:** ```tsx "use client"; export function MyComponent() { const [data, setData] = useState(null); useEffect(() => { fetch("/api/data").then(setData); }, []); return <div>{data}</div>; } ``` **Why:** Client Components in Next.js App Router cannot be async. They run in the browser and should use hooks like `useEffect` for data fetching. --- ### `use-client-must-exist-with-hooks` Ensures that files using React hooks have the `"use client"` directive. āŒ **Incorrect:** ```tsx export function MyComponent() { const [count, setCount] = useState(0); return <button onClick={() => setCount(count + 1)}>{count}</button>; } ``` āœ… **Correct:** ```tsx "use client"; export function MyComponent() { const [count, setCount] = useState(0); return <button onClick={() => setCount(count + 1)}>{count}</button>; } ``` **Why:** React hooks only work in Client Components. Without the `"use client"` directive, the component will be treated as a Server Component, causing runtime errors. **Note:** By default, this rule only applies to files in `components/`, `features/`, and `hooks/` directories, excluding test and storybook files. You can customize these directories using the rule options (see Configuration section below). --- ### `use-server-must-return-promise` Ensures that exported functions in files with `"use server"` directive return a Promise or are async. āŒ **Incorrect:** ```tsx "use server"; export const submitForm = (data: FormData) => { const name = data.get("name"); return { success: true }; }; ``` āœ… **Correct:** ```tsx "use server"; export const submitForm = async (data: FormData) => { const name = data.get("name"); await db.insert({ name }); return { success: true }; }; ``` **Why:** Server Actions in Next.js automatically serialize return values, which requires them to return Promises. Non-async functions will be wrapped in a Promise automatically, but making this explicit prevents confusion and potential bugs. Reference: [Next.js PR #62821](https://github.com/vercel/next.js/pull/62821) ## Configuration ### Recommended Configuration The plugin provides a recommended configuration that enables all rules with their default settings: ```json { "extends": ["plugin:nextjs-app-router/recommended"] } ``` ### Custom Configuration You can customize each rule individually: ```json { "plugins": ["nextjs-app-router"], "rules": { "nextjs-app-router/no-async-client-component": "warn", "nextjs-app-router/use-client-must-exist-with-hooks": "error", "nextjs-app-router/use-server-must-return-promise": "off" } } ``` ### Rule-Specific Options #### `use-client-must-exist-with-hooks` You can customize which directories and file patterns this rule applies to: ```json { "plugins": ["nextjs-app-router"], "rules": { "nextjs-app-router/use-client-must-exist-with-hooks": [ "error", { "targetDirs": ["components", "features", "hooks", "ui"], "excludePatterns": [".stories", ".test", ".spec"] } ] } } ``` **Options:** - `targetDirs` (array of strings): Directory names to target. Default: `["components", "features", "hooks"]` - `excludePatterns` (array of strings): File patterns to exclude. Default: `[".stories", ".test"]` **Examples:** Target all directories: ```json { "rules": { "nextjs-app-router/use-client-must-exist-with-hooks": [ "error", { "targetDirs": ["*"] } ] } } ``` Only target `components` and `ui` directories: ```json { "rules": { "nextjs-app-router/use-client-must-exist-with-hooks": [ "error", { "targetDirs": ["components", "ui"] } ] } } ``` Exclude additional patterns like `.mock` files: ```json { "rules": { "nextjs-app-router/use-client-must-exist-with-hooks": [ "error", { "excludePatterns": [".stories", ".test", ".spec", ".mock"] } ] } } ``` ## Why This Plugin? Next.js App Router introduces a new paradigm with Server Components and Client Components. This separation can be confusing, especially for developers new to the App Router. This plugin helps you: - **Avoid common pitfalls** when working with Server and Client Components - **Catch errors at lint time** instead of runtime - **Learn best practices** through clear error messages - **Maintain consistency** across your codebase ## Requirements - ESLint 7.0.0 or higher - Next.js 13+ (App Router) - Bun 1.2.0+ or Node.js 22.0.0+ ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. ## License MIT Ā© Masahiro Asahara ## Related - [Next.js App Router Documentation](https://nextjs.org/docs/app) - [Next.js Server Components](https://nextjs.org/docs/app/building-your-application/rendering/server-components) - [Next.js Client Components](https://nextjs.org/docs/app/building-your-application/rendering/client-components) - [Next.js Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations)