React TypeScript Error: Handling Multiple Children in JSX
Problem Statement
When working with React and TypeScript, developers often encounter the error:
This JSX tag's 'children' prop expects a single child of type 'Element | undefined', but multiple children were provided.
This typically occurs when you pass multiple elements as children to a component, but the component's TypeScript interface doesn't properly define the children prop type to accept multiple elements. The error is particularly common when:
- Creating wrapper components that need to accept multiple child elements
- Using conditional rendering patterns with multiple elements
- Upgrading to React 18 where children typing has changed
Solutions
1. Use React.ReactNode Type (Recommended)
The most robust solution is to use React.ReactNode
as the type for children, which accepts all valid React child types:
export interface IInputWrapperProps {
label?: string;
required?: boolean;
minimizedLabel?: boolean;
description?: string;
error?: string;
wrapperStyle?: React.CSSProperties;
children?: React.ReactNode; // Changed from JSX.Element
}
React.ReactNode
accepts:
- JSX elements
- Strings and numbers
- Arrays of elements
- Fragments (
<>...</>
) - Booleans, null, and undefined (which are not rendered)
2. Use React.PropsWithChildren Utility Type
For functional components, you can use React's built-in PropsWithChildren
type:
import { PropsWithChildren } from 'react';
export interface IInputWrapperProps {
label?: string;
required?: boolean;
minimizedLabel?: boolean;
description?: string;
error?: string;
wrapperStyle?: React.CSSProperties;
}
export default function InputWrapper({
label,
error,
description,
children,
required,
wrapperStyle,
minimizedLabel
}: PropsWithChildren<IInputWrapperProps>) {
// Component implementation
}
3. Wrap Multiple Children in a Fragment
If you need to pass multiple elements but the component expects a single child, wrap them in a React Fragment:
<InputWrapper label={label} error={error} {...rest}>
<>
<Passwordinput
label={label}
type={state ? "text" : "password"}
onChange={e => onChange(e.target.value)}
value={value}
error={error}
/>
<Svg>
<img
onClick={() => setstate(state => !state)}
style={{ cursor: "pointer" }}
src={state ? eyeShow : eyeHide}
alt="searchIcon"
/>
</Svg>
</>
</InputWrapper>
4. Handle Conditional Rendering Properly
When using conditional rendering with unknown types, ensure proper typing:
<Container>
{maybeNull && <Component notNullThing={maybeNull} />}
{maybeNull2 && <Component notNullThing={maybeNull2} />}
</Container>
If maybeNull
is typed as unknown
, TypeScript may not recognize the conditional pattern. Fix it with:
<Container>
{maybeNull ? <Component notNullThing={maybeNull} /> : null}
{maybeNull2 ? <Component notNullThing={maybeNull2} /> : null}
</Container>
5. Proper Props Destructuring
Ensure you're properly destructuring props, especially when using PropsWithChildren
:
// ❌ Incorrect - children is the entire props object
export const MyComponent: FC<PropsWithChildren> = (children) => (
<div>{children}</div>
);
// ✅ Correct - destructure children from props
export const MyComponent: FC<PropsWithChildren> = ({ children }) => (
<div>{children}</div>
);
Common Pitfalls to Avoid
AVOID USING any
FOR CHILDREN
While children?: any
might seem like a quick fix, it removes TypeScript's type safety and should be avoided.
// ❌ Not recommended - loses type safety
interface InputWrapperProps {
children?: any;
}
BEWARE OF COMMENTS IN JSX
Comments inside JSX can sometimes be interpreted as children in TypeScript's type system. If you encounter this error unexpectedly, check for comments that might be causing the issue.
Best Practices Summary
- Use
React.ReactNode
for maximum flexibility in children types - Prefer
PropsWithChildren
for functional components - Wrap multiple elements in fragments when needed
- Handle conditional rendering with proper null checking
- Always destructure props correctly when using TypeScript
By following these practices, you'll avoid the "multiple children" error while maintaining strong type safety in your React TypeScript applications.