Article summary
When building a React component, I ran into this error:
Objects are not valid as a React child (found: object with keys... Invariant Violation: Objects are not valid as a React child (found: object ...). If you meant to render a collection of children, use an array instead....
Here is a simple component to illustrate the problem.
import * as React from "react";
type Status =
| "Open"
| "Closed"
type PureProps = {
status: Status;
};
function StatusDescription(status: Status): JSX.Element {
return <span>{status}</span>;
}
export class StatusSummary extends React.PureComponent {
render() {
return (
<StatusDescription status={this.props.status} />
);
}
}
Looking for the Problem
The issue appeared to be in the type of Status
and how it was being rendered.
I tried a few different iterations, first rendering the string literal directly inside the component. My thinking was that the type Status
may not be rendering correctly if TypeScript was mistaking it for an object. When I replaced the props with Status.Closed
and saw the error disappear and the correct text render on the page, I was ensured that it was not an issue with the type definition of Status
or the way it is rendered.
However, this does not solve the problem:
function StatusDescription: JSX.Element {
return {Status.Closed}; // Error gone and component renders, but problem remains
}
Next, I replaced the type Status
with string
, but the error persisted. Passing in a string
and continuing to see an error told me that the props themselves were incorrect, but perhaps not in the way that I was expecting.
function StatusDescription(status: string): JSX.Element {
return {status}; // Error persists
}
Solution
In the end, the problem turned out to be not with the StatusDescription
function, but rather with the props I was passing it.
Note that <StatusDescription status={status} />
is being used as a React component, which expects an object with the needed props. Here, TypeScript did not give any type errors, but instead interpreted status
as an object. This caused problems later on when trying to render the string literal status
in the `JSX.Element`.
The first change necessary was to define an object that contains the props the component needs.
function StatusDescription(props: {
status: Status;
}): JSX.Element {
return {props.status};
}
Now it is valid to use StatusDescription
as a React component.
A similar approach, which I find easier to read, is to define the Props type separately.
type ComponentProps = {
status: Status;
// Whatever else is needed
}
function StatusDescription(props: ComponentProps): JSX.Element {
return {props.status};
}
Lesson Learned
Seeing an Objects are not valid as a React child
error might mean that a few different things are going wrong. For example, it could be because the items being rendered in the JSX Element are objects instead of primitives (so make sure you’re not passing an object where a primitive is expected).
In this case, however, I was reminded that React components always expect to take an object that contains the props it needs. I was burned by this because the component only needs one prop (a string), which I was trying to directly pass in. By making a simple change to the props type of the component, I was able to make the error disappear.
Sentence 2 in “Lesson Learned” solved my problem. Thank you!
Glad to help!
I was hoping this article would also help solve mine, and so far I’m still trying to find which component might be causing the issue. Is there a way to see which component is breaking it?
Everything seemed to be going fine, the app was running and then suddenly after a refresh it stopped working. I rolled back the changes, dropped it to last working commit and still getting the error.
Any hints on troubleshooting the troubled component?
This comment https://github.com/facebook/react-native/issues/18542#issuecomment-428157608 helped me resolve the issue.
Thank you! Good article!
Thank you for reading and finding it helpful!
Just a heads up, I noticed immediately that you were passing “status” into the `StatusDescription` component without destructuring it. If you are going to pass a prop into a functional component this way you need to destructure it. So doing the following would have also solved your issue. If you don’t destructure props being passed into the component the first argument is “props” or simply put the object that contains status.
function StatusDescription({status}: Status): JSX.Element {
return {status};;
}
Hi Ryan,
Thank you for reading this post! Destructuring props is another valid way of passing data down and could help with the issue here. Thanks for the input of a stylistically different approach!
Hi Meredith,
thanks for the post, I got the same issue when changed api call and related infrastructure and my code was suddenly spitting a date object into the jsx instead of the previous date formatted string. The error message given wasn’t very helpful at the beginning, this helped me to track it down!
Cheers,
Vocko
Hi Vocko,
Glad you found it helpful!
Thank you – helped me resolve issue!
Hi Sean,
Great! Happy it helped.