ReactNode
ReactNode is a type that describes what React can render. It's a union of every value React accepts as a child:
ReactElement(the result of JSX,createElement, orcloneElement)stringnumberbigintboolean(trueandfalserender as nothing)nullundefinedIterable<ReactNode>(so arrays of nodes, but also any iterable)ReactPortalPromise<ReactNode>(for async Server Components — React unwraps the promise via Suspense)
Parameters
ReactNode does not take any parameters.
Usage
Typing children
The most common use case for ReactNode is typing children.
import { ReactNode } from "react";
interface Props {
children?: ReactNode;
}
function Component({ children }: Props) {
return children;
}
<Component> accepts anything that React can render as children:
function Examples() {
return (
<>
<Component>
<div>Hello</div>
</Component>
<Component>Hello</Component>
<Component>{123}</Component>
<Component>{42n}</Component>
<Component>
<>Hello</>
</Component>
<Component>{true}</Component>
<Component>{null}</Component>
<Component>{undefined}</Component>
<Component>{[1, 2, 3]}</Component>
</>
);
}
Async Server Components
A Server Component can be async and return a Promise<ReactNode>. React unwraps the promise through the nearest <Suspense> boundary:
// Server Component
async function UserProfile({ userId }: { userId: string }) {
const user = await fetchUser(userId);
return <p>{user.name}</p>;
}
function Page() {
return (
<Suspense fallback={<p>Loading…</p>}>
<UserProfile userId="42" />
</Suspense>
);
}
A bare Promise<ReactNode> can also be passed as children directly — useful for streaming patterns where the parent kicks off the work and the child resolves it:
function Page() {
const userPromise = fetchUser(); // Promise<ReactNode>
return <Suspense fallback={<Loading />}>{userPromise}</Suspense>;
}
ReactNode vs ReactElement vs JSX.Element
These three types are often confused because all three appear when you write JSX. They are not interchangeable:
ReactNodeis the broadest: anything React can render, including primitives,null, arrays, and elements.ReactElementdescribes only the object produced by JSX orcreateElement— it hastype,props, andkey. Astringis not aReactElement.React.JSX.Elementis essentiallyReactElement<any, any>— what the JSX transform infers for a JSX expression.
Use ReactNode for children
ReactNode is the correct type for any prop that receives children-like content, because a caller might pass a string, an array, or null:
type Props = { content: ReactNode };
<MyComponent content="hello" /> // ✅ string is a ReactNode
<MyComponent content={<span>hi</span>} /> // ✅ element is a ReactNode
<MyComponent content={null} /> // ✅ null is a ReactNode
Don't use ReactNode as a function-component return type
A function component's return type should be what React allows components to return, not what it allows them to receive. Returning a plain ReactNode (which includes bigint, Promise<ReactNode>, etc.) is broader than what TypeScript wants to see from a JSX-rendered component. Let TypeScript infer the return type, or use React.JSX.Element / ReactElement if you must annotate:
// 👎 too broad, and historically caused issues when used in JSX
const MyComponent = (): ReactNode => "hello";
// 👍 let TS infer
const MyComponent = () => "hello";
// 👍 explicit
const MyComponent = (): React.JSX.Element => <span>hello</span>;