wanner.work|rules

@wanner.work/oxlint-rules

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

src/hooks/useOnlineStatus.ts
export default function useOnlineStatus(): { online: boolean } {
  return { online: true }
}

Don't

src/hooks/onlineStatus.ts
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

src/hooks/useConnectionState.ts
export default function useConnectionState(): { connected: boolean } {
  return { connected: true }
}

Don't

src/hooks/useConnectionState.ts
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

src/hooks/useCurrentUser.ts
export default function useCurrentUser(): { id: string } {
  return { id: 'u-1' }
}

Don't

src/hooks/useCurrentUser.ts
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

src/hooks/useFeatureToggle.ts
export default function useFeatureToggle(): { enabled: boolean } {
  return { enabled: true }
}

Don't

src/hooks/useFeatureToggle.ts
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

src/hooks/useViewport.ts
function useViewport(): { width: number } {
  return { width: 1024 }
}

export default useViewport

Don't

src/hooks/useViewport.ts
const useViewport = (): { width: number } => {
  return { width: 1024 }
}

export default useViewport

Inline default-exported arrow functions are rejected for the same reason — no name is attached to the default export:

src/hooks/useViewport.ts
// 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

src/hooks/useFilters.ts
interface Options {
  includeArchived: boolean
}

export default function useFilters(options: Options): { active: boolean } {
  return { active: !options.includeArchived }
}

Don't

src/hooks/useNotifications.ts
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:

src/hooks/useNotifications.ts
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 Options interface must not be exported from a hook file.
  • An Options type imported from anywhere other than a /types/ folder is rejected.
  • A hook that uses an Options parameter must either declare a local Options interface 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.

On this page