Bumps Jest to latest 30.x The `resolutions` pin that kept jsdom at 22.1.0 is removed, so the test environment now runs the jsdom version that jest-environment-jsdom declares (26.1.0 on Jest 30). The matcher aliases that Jest 30 deleted are replaced with their canonical forms across the test suites: `toBeCalled`, `toBeCalledTimes`, `toBeCalledWith`, and `lastCalledWith` become the corresponding `toHaveBeenCalled*` matchers, and `toThrowError` becomes `toThrow`. The custom `toThrow` matcher override is removed in the PR below this one. Jest 30 activates the `node` export condition for CommonJS requires in every test environment, so in the jsdom-based Flight suites `react-server-dom-webpack/client` now resolves to the Node build (which requires an options argument) instead of the browser build. Those suites now map the client entry to `client.browser` explicitly, matching the existing mocks for the `server` and `static` entries, and the Turbopack Node test uses `jest.requireActual` because `client` and `client.node` now resolve to the same file, which otherwise made the mock factory recurse. For the same reason `react-dom/static` resolves to the lazily-initialized Node entry in source mode, so its version-mismatch test is gated to build mode like the other server-entry tests. The obsolete `prettierPath` override is dropped from the base Jest config because Jest 30 works with Prettier 3 for inline snapshots, which also fixes `yarn test -u` crashing on the repo's Prettier 3-only hermes plugin, and the now unused `prettier-2` alias dependency is removed with it. Snapshot files are regenerated for Jest 30's updated snapshot header and formatting. One test now passes a number instead of a string to `jest.advanceTimersByTime`, which fake-timers v13 no longer coerces. --------- Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
eslint-plugin-react-hooks
The official ESLint plugin for React which enforces the Rules of React and other best practices.
Installation
Assuming you already have ESLint installed, run:
# npm
npm install eslint-plugin-react-hooks --save-dev
# yarn
yarn add eslint-plugin-react-hooks --dev
Flat Config (eslint.config.js|ts)
Add the recommended config for all recommended rules:
// eslint.config.js
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';
export default defineConfig([
reactHooks.configs.flat.recommended,
]);
If you want to try bleeding edge experimental compiler rules, use recommended-latest.
// eslint.config.js
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';
export default defineConfig([
reactHooks.configs.flat['recommended-latest'],
]);
Legacy Config (.eslintrc)
If you are still using ESLint below 9.0.0, the recommended preset can also be used to enable all recommended rules.
{
"extends": ["plugin:react-hooks/recommended"],
// ...
}
Custom Configuration
If you want more fine-grained configuration, you can instead choose to enable specific rules. However, we strongly encourage using the recommended presets — see above — so that you will automatically receive new recommended rules as we add them in future versions of the plugin.
Flat Config (eslint.config.js|ts)
import reactHooks from 'eslint-plugin-react-hooks';
export default [
{
files: ['**/*.{js,jsx}'],
plugins: { 'react-hooks': reactHooks },
// ...
rules: {
// Core hooks rules
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
// React Compiler rules
'react-hooks/config': 'error',
'react-hooks/error-boundaries': 'error',
'react-hooks/gating': 'error',
'react-hooks/globals': 'error',
'react-hooks/immutability': 'error',
'react-hooks/preserve-manual-memoization': 'error',
'react-hooks/purity': 'error',
'react-hooks/refs': 'error',
'react-hooks/set-state-in-effect': 'error',
'react-hooks/set-state-in-render': 'error',
'react-hooks/static-components': 'error',
'react-hooks/unsupported-syntax': 'warn',
'react-hooks/use-memo': 'error',
'react-hooks/incompatible-library': 'warn',
}
},
];
Legacy Config (.eslintrc)
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
// Core hooks rules
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
// React Compiler rules
"react-hooks/config": "error",
"react-hooks/error-boundaries": "error",
"react-hooks/gating": "error",
"react-hooks/globals": "error",
"react-hooks/immutability": "error",
"react-hooks/preserve-manual-memoization": "error",
"react-hooks/purity": "error",
"react-hooks/refs": "error",
"react-hooks/set-state-in-effect": "error",
"react-hooks/set-state-in-render": "error",
"react-hooks/static-components": "error",
"react-hooks/unsupported-syntax": "warn",
"react-hooks/use-memo": "error",
"react-hooks/incompatible-library": "warn"
}
}
Advanced Configuration
exhaustive-deps can be configured to validate dependencies of custom Hooks with the additionalHooks option.
This option accepts a regex to match the names of custom Hooks that have dependencies.
{
rules: {
// ...
"react-hooks/exhaustive-deps": ["warn", {
additionalHooks: "(useMyCustomHook|useMyOtherCustomHook)"
}]
}
}
We suggest to use this option very sparingly, if at all. Generally saying, we recommend most custom Hooks to not use the dependencies argument, and instead provide a higher-level API that is more focused around a specific use case.
Valid and Invalid Examples
Please refer to the Rules of Hooks documentation to learn more about this rule.
License
MIT