The "React does not recognize the prop" warning occurs when you pass custom props to DOM elements that React doesn't recognize as valid HTML attributes. This happens because React filters out non-standard props before rendering to prevent invalid HTML attributes from appearing in the DOM.
This warning is React's way of telling you that you're trying to pass a prop to a DOM element that isn't a valid HTML attribute. React maintains a list of known HTML attributes and filters out anything that isn't on that list before rendering elements to the DOM. When you create a React component that renders to a DOM element (like `<div>`, `<input>`, or `<button>`), any props you pass that aren't valid HTML attributes will trigger this warning. React does this to prevent invalid HTML from being rendered, which could cause browser errors or unexpected behavior. This commonly happens when you create wrapper components that pass props through to underlying DOM elements, or when using third-party libraries that add custom props. The warning itself doesn't break your application, but it indicates a potential issue with your component design that could lead to bugs or maintenance problems.
When creating components that render DOM elements, extract custom props before passing the rest to the DOM element:
// ❌ WRONG - passing custom prop to div
function MyComponent({ isActive, children }) {
return (
<div isActive={isActive}>
{children}
</div>
);
}
// ✅ CORRECT - extract custom prop
function MyComponent({ isActive, children }) {
return (
<div className={isActive ? 'active' : ''}>
{children}
</div>
);
}
// ✅ CORRECT - using data-* attribute for custom data
function MyComponent({ isActive, children }) {
return (
<div data-active={isActive}>
{children}
</div>
);
}Data attributes (prefixed with data-) are valid HTML and won't trigger warnings.
When using ...props on a DOM element, filter out non-DOM props first:
// ❌ WRONG - spreads all props including custom ones
function Button({ children, ...props }) {
return <button {...props}>{children}</button>;
}
// ✅ CORRECT - filter custom props
function Button({ variant, size, children, ...props }) {
// variant and size are extracted, only DOM props remain in ...props
const className = `btn btn-${variant} btn-${size}`;
return (
<button className={className} {...props}>
{children}
</button>
);
}
// ✅ CORRECT - using a helper function to filter props
import { pickHTMLAttributes } from './utils';
function MyComponent(props) {
const htmlProps = pickHTMLAttributes(props);
const customProps = { /* extract custom props */ };
return <div {...htmlProps}>{/* ... */}</div>;
}Only spread props that are valid HTML attributes.
Many custom props can be replaced with standard HTML attributes:
// Instead of custom props, use standard attributes:
// ❌ Custom boolean prop
<div isVisible={true}>
// ✅ Use hidden attribute or CSS class
<div hidden={!isVisible}>
<div className={isVisible ? 'visible' : 'hidden'}>
// ❌ Custom string prop
<div variant="primary">
// ✅ Use data-* attribute or CSS class
<div data-variant="primary">
<div className="variant-primary">
// ❌ Custom event-like prop
<div onCustomEvent={handler}>
// ✅ Use standard events or data-* with event delegation
div.addEventListener('click", handler)
// Or pass as data attribute and handle in parent
<div data-custom-event="true">Check MDN for valid HTML attributes for each element type.
When creating wrapper components, explicitly handle which props go where:
// Good pattern for wrapper components
function InputWrapper({
label,
error,
helperText,
// Extract Input-specific props
value,
onChange,
type,
// DOM props for the wrapper div
className,
style,
...rest
}) {
return (
<div className={`input-wrapper ${className}`} style={style}>
{label && <label>{label}</label>}
<input
value={value}
onChange={onChange}
type={type}
{...rest} // Only DOM props for input remain
/>
{error && <div className="error">{error}</div>}
{helperText && <div className="helper-text">{helperText}</div>}
</div>
);
}
// Usage
<InputWrapper
label="Email"
value={email}
onChange={handleEmailChange}
type="email"
placeholder="Enter your email"
required
/>This pattern clearly separates wrapper props from DOM element props.
When using third-party components that cause prop warnings:
// If a library component passes invalid props:
import { ThirdPartyButton } from 'some-library';
// Check if the library has a prop filter option
<ThirdPartyButton
variant="primary"
filterInvalidProps={true}
/>
// Or wrap it to filter props yourself
function SafeButton(props) {
const { variant, size, ...rest } = props;
const className = `btn-${variant} btn-${size}`;
return (
<ThirdPartyButton
className={className}
{...rest}
/>
);
}
// For styled-components or emotion, use transient props
const StyledButton = styled.button`
// Use transient prop syntax ($$ prefix)
background: ${props => props.$variant === 'primary' ? 'blue' : 'gray'};
`;
// Usage with transient prop
<StyledButton $variant="primary">Click</StyledButton>Check the library documentation for prop filtering options.
When creating components that need ref forwarding:
// ❌ WRONG - ref gets passed as prop
function MyInput(props) {
return <input {...props} />;
}
// ✅ CORRECT - use forwardRef
const MyInput = React.forwardRef(function MyInput(props, ref) {
const { variant, size, ...rest } = props;
const className = `input input-${variant} input-${size}`;
return (
<input
ref={ref}
className={className}
{...rest}
/>
);
});
// ✅ CORRECT - TypeScript with forwardRef
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
variant?: 'default' | 'primary';
size?: 'sm' | 'md' | 'lg';
}
const MyInput = React.forwardRef<HTMLInputElement, InputProps>(
({ variant = 'default', size = 'md', ...props }, ref) => {
const className = `input input-${variant} input-${size}`;
return <input ref={ref} className={className} {...props} />;
}
);forwardRef properly handles the ref prop without passing it to DOM.
Use ESLint rules to catch prop issues during development:
npm install --save-dev eslint-plugin-reactAdd to your .eslintrc.js:
module.exports = {
plugins: ['react'],
rules: {
'react/no-unknown-property': ['error', { ignore: ['data-*', 'aria-*'] }],
'react/jsx-props-no-spreading': ['error', {
html: 'ignore',
custom: 'enforce',
exceptions: []
}]
}
};For TypeScript, ensure your prop types extend the correct HTML attributes:
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary';
// Custom props are separated from HTML props
}These rules help prevent prop warnings before runtime.
In React 19+, there's improved handling of custom props with the use hook and better prop filtering. However, the fundamental rule remains: DOM elements should only receive valid HTML attributes.
For complex component libraries, consider using a prop filtering utility:
function filterDOMProps(props: Record<string, any>) {
const domProps: Record<string, any> = {};
const validAttributes = [
// List of valid HTML attributes
'className', 'style', 'id', 'title', 'tabIndex',
// All standard HTML attributes...
];
for (const key in props) {
if (validAttributes.includes(key) || key.startsWith('data-') || key.startsWith('aria-')) {
domProps[key] = props[key];
}
}
return domProps;
}Remember that data-* and aria-* attributes are always valid and won't trigger warnings. Use them for custom data and accessibility attributes.
React.FC expects children prop to be defined
React.FC no longer includes implicit children prop
Warning: You provided a `selected` prop to a form field without an `onChange` handler
You provided a 'selected' prop without an onChange handler
Failed to load source map from suspense chunk
How to fix "Failed to load source map from suspense chunk" in React
Prop spreading could cause security issues
Prop spreading could cause security issues
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency