no-clone-element
Disallows 'cloneElement'.
Full Name in eslint-plugin-react-x
react-x/no-clone-elementFull Name in @eslint-react/eslint-plugin
@eslint-react/no-clone-elementPresets
x
recommended
recommended-typescript
recommended-type-checked
strict
strict-typescript
strict-type-checked
Rule Details
Using cloneElement is uncommon and can lead to fragile code. It also makes it harder to trace data flow. Try the alternatives instead.
Examples
Augmenting an element's props dynamically
Using cloneElement to inject props into a child element hides the data flow and breaks component encapsulation. Prefer passing props explicitly through render props or composition patterns.
// Problem: Using cloneElement to dynamically inject props into a child element
import { cloneElement } from "react";
const clonedElement = cloneElement(
<Row title="Cabbage">Hello</Row>,
{ isHighlighted: true },
"Goodbye",
);
console.log(clonedElement); // <Row title="Cabbage" isHighlighted={true}>Goodbye</Row>Wrapping children with injected props
Using cloneElement inside Children.map hides which props a child receives and breaks encapsulation. Prefer passing data explicitly via render props or by restructuring your component API.
// Problem: Using cloneElement to inject props into each child
import { Children, cloneElement, useState } from "react";
function List({ children }) {
const [selectedIndex, setSelectedIndex] = useState(0);
return (
<div className="List">
{Children.map(children, (child, index) =>
cloneElement(child, {
isHighlighted: index === selectedIndex,
}),
)}
<button onClick={() => setSelectedIndex((i) => (i + 1) % Children.count(children))}>
Next
</button>
</div>
);
}// Recommended: Pass isHighlighted explicitly via render props
import { useState } from "react";
function List({ items, renderItem }) {
const [selectedIndex, setSelectedIndex] = useState(0);
return (
<div className="List">
{items.map((item, index) => {
const isHighlighted = index === selectedIndex;
return renderItem(item, isHighlighted);
})}
<button onClick={() => setSelectedIndex((i) => (i + 1) % items.length)}>
Next
</button>
</div>
);
}
function App() {
return (
<List
items={products}
renderItem={(product, isHighlighted) => (
<Row key={product.id} title={product.title} isHighlighted={isHighlighted} />
)}
/>
);
}Using component composition instead of cloneElement
Instead of injecting props into children with cloneElement, compose components so that the parent controls the layout and passes props explicitly.
// Recommended: Use component composition to wrap and configure items
interface RowProps {
title: string;
isHighlighted?: boolean;
children: React.ReactNode;
}
function Row({ title, isHighlighted, children }: RowProps) {
return (
<div className={isHighlighted ? "Row highlighted" : "Row"}>
<h3>{title}</h3>
{children}
</div>
);
}
function App() {
return (
<div className="List">
<Row title="First" isHighlighted>
<p>First item content</p>
</Row>
<Row title="Second">
<p>Second item content</p>
</Row>
</div>
);
}