Prefer Namespace Import
Enforces importing React via a namespace import
Overview
This rule enforces that React is imported via a namespace import (import * as React from 'react') instead of a default import (import React from 'react'). It provides an auto-fix that converts default imports to namespace imports.
The rule respects:
- The configured
importSourcefrom React settings (e.g.,"@pika/react") - Type imports (
import type React from 'react'→import type * as React from 'react') - Quote style (single or double)
- Semicolon usage style
Rule
Copy the following code into your project (e.g. .config/preferNamespaceImport.ts):
import type { RuleFunction } from "@eslint-react/kit";
import type { TSESTree } from "@typescript-eslint/types";
/** Enforce importing React via a namespace import. */
export function preferNamespaceImport(): RuleFunction {
return (context, { settings }) => {
const { importSource } = settings;
return {
[`ImportDeclaration[source.value="${importSource}"] ImportDefaultSpecifier`](node: TSESTree.ImportDefaultSpecifier) {
const hasOtherSpecifiers = node.parent.specifiers.length > 1;
context.report({
data: { importSource },
fix(fixer) {
const importDeclarationText = context.sourceCode.getText(node.parent);
const semi = importDeclarationText.endsWith(";") ? ";" : "";
const quote = node.parent.source.raw?.at(0) ?? "'";
const isTypeImport = node.parent.importKind === "type";
const importStringPrefix = `import${isTypeImport ? " type" : ""}`;
const importSourceQuoted = `${quote}${importSource}${quote}`;
if (!hasOtherSpecifiers) {
return fixer.replaceText(
node.parent,
`${importStringPrefix} * as ${node.local.name} from ${importSourceQuoted}${semi}`,
);
}
// remove the default specifier and prepend the namespace import specifier
const specifiers = importDeclarationText.slice(
importDeclarationText.indexOf("{"),
importDeclarationText.indexOf("}") + 1,
);
return fixer.replaceText(
node.parent,
[
`${importStringPrefix} * as ${node.local.name} from ${importSourceQuoted}${semi}`,
`${importStringPrefix} ${specifiers} from ${importSourceQuoted}${semi}`,
].join("\n"),
);
},
message: `Prefer importing React as 'import * as React from "${importSource}"' `,
node: hasOtherSpecifiers ? node : node.parent,
});
},
};
};
}Config
import eslintReactKit from "@eslint-react/kit";
import { preferNamespaceImport } from "./.config/preferNamespaceImport";
export default [
// ... other configs
{
...eslintReactKit()
.use(preferNamespaceImport)
.getConfig(),
files: ["src/**/*.{ts,tsx}"],
},
];Examples
Default import from React
// Problem: default import is discouraged.
import React from 'react';// Problem: default import is discouraged.
import React, { useState, useEffect } from 'react';Namespace import from React
// Recommended: namespace import is preferred.
import * as React from 'react';// OK: named imports without default import are fine.
import { useState, useEffect } from 'react';Resources
- AST Explorer - A tool for exploring the abstract syntax tree (AST) of JavaScript code, which is essential for writing custom rules.
- ESLint Developer Guide - Official ESLint documentation for creating custom rules.
- Using the TypeScript Compiler API - TypeScript compiler API documentation for working with type information in custom rules.
No Circular Effect
Detects circular dependencies between useEffect hooks. Prevents infinite update loops caused by effects that set state they also depend on
Custom Rules Of Props
Validates JSX props. Includes checks for duplicate props, mixing controlled and uncontrolled props, explicit spread props, and direct props access