Hook Rules
All rules regarding react hooks
Rules that govern how React hook files are written. Every rule is enabled by the
recommended config and applies to .ts / .tsx files inside a
/hooks/ folder of your project.
hook-naming-convention
Require top-level hook names to start with use — the React convention that
also tells linters and React DevTools which functions are hooks.
Do
export default function useOnlineStatus(): { online: boolean } {
return { online: true }
}Don't
export default function onlineStatus(): { online: boolean } {
return { online: true }
}The rule flags any top-level function declaration, function variable, or
exported function whose name does not start with use.
hook-filename-matches-name
Require hook file names to match hook names (excluding extension).
Do
export default function useConnectionState(): { connected: boolean } {
return { connected: true }
}Don't
export default function useNetworkStatus(): { connected: boolean } {
return { connected: true }
}A matching file name keeps the file locator and the import name in sync, which matters a lot for hooks that are used in many places.
hook-one-per-file
Require hook files to contain only one top-level hook.
Do
export default function useCurrentUser(): { id: string } {
return { id: 'u-1' }
}Don't
export default function useCurrentUser(): { id: string } {
return { id: 'u-1' }
}
function useSessionToken(): string {
return 'token'
}
If you have a second hook in the same file, move it to its own file under
src/hooks/. This keeps each hook easy to find, easy to test, and easy to
lazy-load.
hook-require-default-export
Require hook files to default export their top-level hook.
Do
export default function useFeatureToggle(): { enabled: boolean } {
return { enabled: true }
}Don't
export function useFeatureToggle(): { enabled: boolean } {
return { enabled: true }
}A named export would force every caller to know the hook's original name. A
default export lets the importer rename the hook to fit the surrounding context
(e.g. useFeatureToggle as useToggle).
hook-default-export-function-declaration
Require the default export in hook files to be a named function declaration.
Do
function useViewport(): { width: number } {
return { width: 1024 }
}
export default useViewportDon't
const useViewport = (): { width: number } => {
return { width: 1024 }
}
export default useViewportInline default-exported arrow functions are rejected for the same reason — no name is attached to the default export:
// No name attached to the default export
export default (): { width: number } => {
return { width: 1024 }
}Function declarations are hoisted, have an explicit name (great for React DevTools and the React hook linter), and are the convention the rest of this rule pack relies on.
hook-options-interface
Allow Options interfaces in hook files only when they are declared locally or
imported from a /types/ folder.
Do
interface Options {
includeArchived: boolean
}
export default function useFilters(options: Options): { active: boolean } {
return { active: !options.includeArchived }
}Don't
import type Options from '../shared/Options'
export default function useNotifications(options: Options): {
enabled: boolean
} {
return { enabled: options.notify }
}Importing Options from a /types/ folder is allowed:
import type Options from '../types/Options'
export default function useNotifications(options: Options): {
enabled: boolean
} {
return { enabled: options.notify }
}The rule enforces all of the following at once:
- An
Optionsinterface must not be exported from a hook file. - An
Optionstype imported from anywhere other than a/types/folder is rejected. - A hook that uses an
Optionsparameter must either declare a localOptionsinterface or import it from/types/.
The /types/ folder can be nested — for example import SubOptions from '../types/sub/SubOptions' is treated as a valid /types/ import. The intent
is to keep option contracts in one obvious place, no matter how deep that
folder happens to be.