React 18.3+ warns that defaultProps support will be removed from function components in future releases. This deprecation encourages migration to ES6 default parameters, which offer better TypeScript integration and align with modern JavaScript standards.
This deprecation warning appears when you use the `defaultProps` pattern on function components in React 18.3 or later. React is phasing out this legacy API in favor of ES6 default parameters. The `defaultProps` feature was introduced in early React versions before ES6 default parameters were widely adopted. With modern JavaScript, destructuring props with default values provides a cleaner, more efficient alternative that works seamlessly with TypeScript and modern tooling. While the warning won't break your application, it signals that future React versions (particularly React 19+) will remove defaultProps support for function components entirely. Class components will continue to support defaultProps since they don't have an ES6 alternative.
Migrate from the legacy pattern to destructured parameters with defaults:
Before (deprecated):
interface Props {
name: string;
age: number;
greeting: string;
}
function UserProfile(props: Props) {
return <div>{props.greeting}, {props.name}!</div>;
}
UserProfile.defaultProps = {
greeting: 'Hello'
};After (recommended):
interface Props {
name: string;
age: number;
greeting?: string; // Mark as optional
}
function UserProfile({
name,
age,
greeting = 'Hello' // ES6 default parameter
}: Props) {
return <div>{greeting}, {name}!</div>;
}This approach works for both JavaScript and TypeScript and provides better type safety.
For object or array defaults, use the same destructuring pattern:
interface Props {
user: {
name: string;
role?: string;
};
config?: {
theme: string;
locale: string;
};
}
function Dashboard({
user,
config = { theme: 'dark', locale: 'en' } // Object default
}: Props) {
return (
<div>
{user.name} - {config.theme} theme
</div>
);
}For nested destructuring with defaults:
function Settings({
config: {
theme = 'light',
locale = 'en'
} = {} // Fallback for undefined config
}) {
return <div>Theme: {theme}</div>;
}Make props optional in TypeScript when they have defaults:
// Mark defaulted props as optional with ?
interface ButtonProps {
label: string; // Required
variant?: 'primary' | 'secondary'; // Optional with default
size?: 'sm' | 'md' | 'lg'; // Optional with default
disabled?: boolean; // Optional with default
}
function Button({
label,
variant = 'primary',
size = 'md',
disabled = false
}: ButtonProps) {
return (
<button className={`btn-${variant} btn-${size}`} disabled={disabled}>
{label}
</button>
);
}This ensures consumers can omit optional props without TypeScript errors.
For large codebases, use codemods to automate the migration:
Option 1: React-codemod
npx react-codemod default-props-to-default-parameters src/Option 2: Manual find and replace
Search for patterns like:
# Find all defaultProps usage
grep -r ".defaultProps" src/Then refactor each occurrence to use ES6 defaults.
Option 3: ESLint rule
Enable the rule to catch remaining instances:
{
"rules": {
"react/no-default-props": ["error", {
"functionalComponents": "error"
}]
}
}Test that default values work correctly:
import { render, screen } from '@testing-library/react';
function Greeting({ name = 'Guest' }: { name?: string }) {
return <h1>Hello, {name}!</h1>;
}
test('uses default name when prop not provided', () => {
render(<Greeting />);
expect(screen.getByText('Hello, Guest!')).toBeInTheDocument();
});
test('uses provided name when prop is given', () => {
render(<Greeting name="Alice" />);
expect(screen.getByText('Hello, Alice!')).toBeInTheDocument();
});Ensure undefined vs null behavior matches expectations.
Class components vs function components:
Class components will continue to support defaultProps indefinitely because they lack an ES6 alternative. Only function components are affected by this deprecation.
Null vs undefined behavior:
ES6 defaults only apply when a prop is undefined. If you explicitly pass null, the default won't apply:
function Example({ value = 'default' }: { value?: string | null }) {
return <div>{value}</div>;
}
<Example /> // Renders "default"
<Example value={undefined} /> // Renders "default"
<Example value={null} /> // Renders empty (null !== undefined)To handle null, use nullish coalescing:
function Example({ value }: { value?: string | null }) {
const displayValue = value ?? 'default';
return <div>{displayValue}</div>;
}Merging default objects:
For deep default objects, consider a merge utility:
import { merge } from 'lodash';
function Component({ config }: { config?: Partial<Config> }) {
const fullConfig = merge({}, DEFAULT_CONFIG, config);
// ...
}React 19 changes:
React 19 completely removes defaultProps support for function components. Code using defaultProps will throw errors rather than warnings. Plan migration before upgrading to React 19.
ForwardRef components:
For components using forwardRef, apply defaults in the inner function:
const Input = forwardRef<HTMLInputElement, InputProps>(
({ placeholder = 'Enter text...', ...props }, ref) => {
return <input ref={ref} placeholder={placeholder} {...props} />;
}
);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