Custom Rules Of Children
Validates React Children API usage.
Overview
This recipe contains five custom rules for validating React Children API usage:
noChildrenCount: Reports the use ofChildren.countfrom the 'react' package.noChildrenForEach: Reports the use ofChildren.forEachfrom the 'react' package.noChildrenMap: Reports the use ofChildren.mapfrom the 'react' package.noChildrenOnly: Reports the use ofChildren.onlyfrom the 'react' package.noChildrenToArray: Reports the use ofChildren.toArrayfrom the 'react' package.
noChildrenCount
Rule
Copy the following into your project (e.g. .config/noChildrenCount.ts):
import type { RuleFunction } from "@eslint-react/kit";
/** Disallow the use of Children.count. */
export function noChildrenCount(): RuleFunction {
return (context, { is }) => ({
CallExpression(node) {
if (is.childrenCountCall(node)) {
context.report({
node,
message: "Using 'Children.count' is uncommon and can lead to fragile code. Use alternatives instead.",
});
}
},
});
}Config
import eslintReactKit from "@eslint-react/kit";
import { noChildrenCount } from "./.config/noChildrenCount";
export default [
// ... other configs
{
...eslintReactKit()
.use(noChildrenCount)
.getConfig(),
files: ["src/**/*.tsx"],
},
];Examples
Invalid
import { Children } from "react";
function MyComponent({ children }) {
return <h1>Total: {Children.count(children)}</h1>;
// ^^^^^ Using 'Children.count' is uncommon and can lead to fragile code. Use alternatives instead.
}Valid
function MyComponent({ children }) {
const count = Array.isArray(children) ? children.length : 0;
return <h1>Total: {count}</h1>;
}Reports when Children.count is used. This API is fragile because it doesn't work well with fragments, renders, and other modern React patterns.
noChildrenForEach
Rule
Copy the following into your project (e.g. .config/noChildrenForEach.ts):
import type { RuleFunction } from "@eslint-react/kit";
/** Disallow the use of Children.forEach. */
export function noChildrenForEach(): RuleFunction {
return (context, { is }) => ({
CallExpression(node) {
if (is.childrenForEachCall(node)) {
context.report({
node,
message: "Using 'Children.forEach' is uncommon and can lead to fragile code. Use alternatives instead.",
});
}
},
});
}Config
import eslintReactKit from "@eslint-react/kit";
import { noChildrenForEach } from "./.config/noChildrenForEach";
export default [
// ... other configs
{
...eslintReactKit()
.use(noChildrenForEach)
.getConfig(),
files: ["src/**/*.tsx"],
},
];Examples
Invalid
import { Children } from "react";
function MyComponent({ children }) {
const result = [];
Children.forEach(children, (child, index) => {
// ^^^^^^^ Using 'Children.forEach' is uncommon and can lead to fragile code. Use alternatives instead.
result.push(child);
result.push(<hr key={index} />);
});
}Valid
function MyComponent({ children }) {
return (
<>
{Array.isArray(children) && children.map((child, index) => (
<React.Fragment key={index}>
{child}
<hr />
</React.Fragment>
))}
</>
);
}Reports when Children.forEach is used. Use standard array methods or JSX transformation patterns instead.
noChildrenMap
Rule
Copy the following into your project (e.g. .config/noChildrenMap.ts):
import type { RuleFunction } from "@eslint-react/kit";
/** Disallow the use of Children.map. */
export function noChildrenMap(): RuleFunction {
return (context, { is }) => ({
CallExpression(node) {
if (is.childrenMapCall(node)) {
context.report({
node,
message: "Using 'Children.map' is uncommon and can lead to fragile code. Use alternatives instead.",
});
}
},
});
}Config
import eslintReactKit from "@eslint-react/kit";
import { noChildrenMap } from "./.config/noChildrenMap";
export default [
// ... other configs
{
...eslintReactKit()
.use(noChildrenMap)
.getConfig(),
files: ["src/**/*.tsx"],
},
];Examples
Invalid
import { Children } from "react";
function MyComponent({ children }) {
return (
<div className="RowList">
{Children.map(children, (child) => <div className="Row">{child}</div>)}
// ^^^ Using 'Children.map' is uncommon and can lead to fragile code. Use alternatives instead.
</div>
);
}Valid
function MyComponent({ children }) {
return (
<div className="RowList">
{Array.isArray(children) && children.map((child, i) => (
<div className="Row" key={i}>{child}</div>
))}
</div>
);
}Reports when Children.map is used. This pattern is rarely needed in modern React; instead, render children directly or use standard array methods.
noChildrenOnly
Rule
Copy the following into your project (e.g. .config/noChildrenOnly.ts):
import type { RuleFunction } from "@eslint-react/kit";
/** Disallow the use of Children.only. */
export function noChildrenOnly(): RuleFunction {
return (context, { is }) => ({
CallExpression(node) {
if (is.childrenOnlyCall(node)) {
context.report({
node,
message: "Using 'Children.only' is uncommon and can lead to fragile code. Use alternatives instead.",
});
}
},
});
}Config
import eslintReactKit from "@eslint-react/kit";
import { noChildrenOnly } from "./.config/noChildrenOnly";
export default [
// ... other configs
{
...eslintReactKit()
.use(noChildrenOnly)
.getConfig(),
files: ["src/**/*.tsx"],
},
];Examples
Invalid
import { Children } from "react";
function MyComponent({ children }) {
const element = Children.only(children);
// ^^^^ Using 'Children.only' is uncommon and can lead to fragile code. Use alternatives instead.
// ...
}Valid
import { isValidElement } from "react";
function MyComponent({ children }) {
if (!isValidElement(children)) {
throw new Error("Expected a single React element child");
}
const element = children;
// ...
}Reports when Children.only is used. This API throws if children is not a single element. Use type checks or explicit validation instead for clearer error handling.
noChildrenToArray
Rule
Copy the following into your project (e.g. .config/noChildrenToArray.ts):
import type { RuleFunction } from "@eslint-react/kit";
/** Disallow the use of Children.toArray. */
export function noChildrenToArray(): RuleFunction {
return (context, { is }) => ({
CallExpression(node) {
if (is.childrenToArrayCall(node)) {
context.report({
node,
message: "Using 'Children.toArray' is uncommon and can lead to fragile code. Use alternatives instead.",
});
}
},
});
}Config
import eslintReactKit from "@eslint-react/kit";
import { noChildrenToArray } from "./.config/noChildrenToArray";
export default [
// ... other configs
{
...eslintReactKit()
.use(noChildrenToArray)
.getConfig(),
files: ["src/**/*.tsx"],
},
];Examples
Invalid
import { Children } from "react";
function MyComponent({ children }) {
const result = Children.toArray(children);
// ^^^^^^^ Using 'Children.toArray' is uncommon and can lead to fragile code. Use alternatives instead.
result.reverse();
// ...
}Valid
function MyComponent({ children }) {
const result = Array.isArray(children) ? [...children] : [children];
// ...
}Reports when Children.toArray is used. This API is typically a workaround for poor component design. Restructure your props or use standard React patterns instead.
Using All Rules
To use all five rules together:
import eslintReactKit from "@eslint-react/kit";
import {
noChildrenCount,
noChildrenForEach,
noChildrenMap,
noChildrenOnly,
noChildrenToArray,
} from "./.config";
export default [
// ... other configs
{
...eslintReactKit()
.use(noChildrenCount)
.use(noChildrenForEach)
.use(noChildrenMap)
.use(noChildrenOnly)
.use(noChildrenToArray)
.getConfig(),
files: ["src/**/*.tsx"],
},
];Further Reading
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.
See Also
custom-rules-of-context
Validates React Context API usage. Includes checks foruseContextand other context-related patterns.