This TypeScript error occurs when attempting to use a string value in a context where React.ReactNode is expected, typically due to overly strict type definitions or incorrect type constraints in component props.
This error appears when TypeScript's type checker detects a type mismatch between a string value and an expected React.ReactNode type. While React.ReactNode is designed to accept strings (along with JSX elements, numbers, arrays, and other renderable content), this error typically surfaces when there are issues with type definitions, generic constraints, or overly restrictive custom types. The error most commonly occurs in scenarios involving generic components, conditional types, or when there are conflicting React type definitions in your project. TypeScript may fail to recognize that strings are valid ReactNode values if the type system has been constrained in a way that excludes primitive types. This is fundamentally a TypeScript type system issue rather than a runtime error - your code may work perfectly fine at runtime, but TypeScript's static analysis is preventing compilation due to perceived type incompatibility.
TypeScript will show you exactly where the type mismatch occurs. Look at the error message to identify:
// Example error location
const MyComponent = ({ children }: { children: SomeCustomType }) => {
return <div>{children}</div>;
// ^^^^^^^^ Type 'string' is not assignable to type 'React.ReactNode'
};
// Usage that triggers error
<MyComponent>Hello World</MyComponent>Check if the error occurs in:
- Component prop definitions
- Generic type parameters
- Return types of render functions
- Custom type aliases that reference ReactNode
Replace custom type definitions with React.ReactNode to ensure full compatibility:
// ❌ Problematic - custom type may be too restrictive
type CustomChildren = JSX.Element | JSX.Element[];
interface Props {
children: CustomChildren; // Won't accept strings
}
// ✅ Correct - use ReactNode for maximum flexibility
interface Props {
children: React.ReactNode; // Accepts strings, elements, arrays, etc.
}
const MyComponent = ({ children }: Props) => {
return <div>{children}</div>;
};
// Now this works
<MyComponent>Hello World</MyComponent>React.ReactNode is a union type that includes: React.ReactElement | string | number | Iterable<React.ReactNode> | React.ReactPortal | boolean | null | undefined
If you're using generics, ensure your constraints don't accidentally exclude strings:
// ❌ Problematic - constraint excludes primitives
function renderContent<T extends JSX.Element>(content: T) {
return <div>{content}</div>;
// ^^^^^^^ Type 'string' not assignable when calling with string
}
// ✅ Correct - constraint allows all valid ReactNode types
function renderContent<T extends React.ReactNode>(content: T) {
return <div>{content}</div>;
}
// Or simply don't constrain at all
function renderContent(content: React.ReactNode) {
return <div>{content}</div>;
}
// Both of these now work
renderContent("Hello");
renderContent(<span>World</span>);Avoid using Extract<React.ReactNode, ...> or Exclude<React.ReactNode, ...> unless you specifically need to narrow the type.
Conditional types can sometimes break the ReactNode union. Simplify or restructure them:
// ❌ Problematic - conditional type may narrow incorrectly
type ComponentChildren<T> = T extends true ? JSX.Element : string;
interface Props<T extends boolean> {
flag: T;
children: ComponentChildren<T>; // Type becomes too specific
}
// ✅ Correct - use union type or ReactNode directly
interface Props {
flag: boolean;
children: React.ReactNode; // Simple and works everywhere
}
// Or if you need different types based on a condition
interface Props {
flag: boolean;
children: JSX.Element | string; // Explicit union
}Avoid complex conditional types for children props unless absolutely necessary.
Check your package.json and lockfile for multiple @types/react versions:
# Check for duplicate type definitions
npm ls @types/react
# If you see multiple versions, remove and reinstall
rm -rf node_modules package-lock.json
npm install
# Or update to latest
npm install --save-dev @types/react@latest @types/react-dom@latestAlso verify your tsconfig.json has correct settings:
{
"compilerOptions": {
"jsx": "react-jsx", // For React 17+
"strict": true,
"esModuleInterop": true,
"skipLibCheck": false // Ensure type checking of declaration files
}
}If the type system is being overly strict and you know the code is correct, use a type assertion:
// When you know a string is valid but TypeScript disagrees
const stringContent: string = "Hello World";
<MyComponent>
{stringContent as React.ReactNode}
</MyComponent>
// Or cast the entire children prop
interface Props {
children: SomeRestrictiveType;
}
const MyComponent = ({ children }: Props) => {
return <div>{children as React.ReactNode}</div>;
};This should be used sparingly - it's better to fix the root type definition issue rather than paper over it with assertions.
Understanding ReactNode's Type Definition:
The React.ReactNode type is defined as:
type ReactNode = ReactElement | string | number | Iterable<ReactNode> | ReactPortal | boolean | null | undefined;This means strings should ALWAYS be assignable to ReactNode. If they're not, something is wrong with your type setup.
Common Pattern: Render Props
This error frequently appears with render props patterns:
// ❌ Problematic
interface Props {
render: (data: string) => JSX.Element; // Too restrictive
}
// ✅ Correct
interface Props {
render: (data: string) => React.ReactNode; // Allows any renderable content
}Generic Component Libraries:
If you're building a component library with complex generics, consider using React.PropsWithChildren:
import { PropsWithChildren } from 'react';
interface BaseProps {
title: string;
}
// Automatically adds children: React.ReactNode
type Props = PropsWithChildren<BaseProps>;
const MyComponent = ({ title, children }: Props) => {
return <div><h1>{title}</h1>{children}</div>;
};TypeScript Utility Types to Avoid:
Be cautious with these utility types when working with ReactNode:
- NonNullable<React.ReactNode> - Excludes null/undefined but still includes string
- Extract<React.ReactNode, object> - Excludes primitives including string
- Exclude<React.ReactNode, string> - Obviously excludes strings
React 18+ Considerations:
React 18 updated some type definitions. If you're seeing this error after upgrading, ensure you're using @types/react@18 or later and that your component definitions follow the new patterns.
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
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite