エラーの内容
コンポーネントがブラウザのコンソールにこのエラーをスローします:
Objects are not valid as a React child. If you meant to render a collection of children, use an array instead.
アプリがクラッシュするか、画面が真っ白になります。スタックトレースはJSX内のどこかを指していますが、実際の原因はたいていその数行上にあります。
根本原因
Reactは文字列、数値、真偽値、配列、Reactエレメントをレンダリングする方法を知っています。しかし、プレーンなJavaScriptオブジェクトやPromiseに対しては、どう処理すればよいかわかりません。これらをJSXの子要素として渡すと、Reactはこのエラーで処理を中断します。
よくある原因:
{user.name}のようなフィールドではなく、オブジェクト全体{user}をレンダリングしているDateオブジェクトをレンダリングしている —new Date()は見た目がそれっぽくても文字列ではありません- Promiseをレンダリングしている — 非同期呼び出しへの
awaitを忘れている - 実際の値にアクセスする前に、APIレスポンスをそのままJSXに渡している
Object.keys()やObject.entries()を呼び出した後、結果に対して.map()するのを忘れている
正確な原因を特定する方法
ブラウザのコンソールでエラーメッセージ全体を確認してください。Reactは意外なほど親切にヒントを示してくれます:
Error: Objects are not valid as a React child (found: object with keys {id, name, email}).
これらのキーがヒントです。コンポーネント内でそのオブジェクトの形がJSXに流れ込んでいる箇所を検索してください。
それでもわからない場合は、returnの直前に簡単なログを追加してみましょう:
console.log(typeof someVariable, someVariable);
typeof が "object" を返し、その変数が子要素として使われている場合 — それが原因です。
修正1 — オブジェクトではなくプロパティにアクセスする
十中八九、これが問題です。必要なのは一つのフィールドだけなのに、オブジェクト全体を渡しています。
// ❌ 間違い — userオブジェクト全体をレンダリングしてしまう
function UserCard({ user }) {
return {user}
;
}
// ✅ 修正済み — 特定のプロパティをレンダリングする
function UserCard({ user }) {
return {user.name}
;
}
修正2 — Dateオブジェクトを文字列に変換する
Date オブジェクトはレンダリングできません。表示形式に応じて .toLocaleDateString()、.toISOString()、または .toLocaleString() などの文字列変換メソッドを使用してください。
// ❌ 間違い
const createdAt = new Date(post.createdAt);
return {createdAt};
// ✅ 修正済み
return {createdAt.toLocaleDateString()};
修正3 — レンダリング前にPromiseをawaitする
非同期関数はPromiseを返します。await なしで呼び出すと、データではなくPromiseオブジェクトが返ってきます。
// ❌ 間違い — fetchUserはユーザーではなくPromiseを返す
function Profile() {
const user = fetchUser();
return {user.name}
;
}
// ✅ 修正済み — useEffect + useStateで非同期のライフサイクルを処理する
function Profile() {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser().then(setUser);
}, []);
if (!user) return Loading...
;
return {user.name}
;
}
修正4 — デバッグ用にJSONを文字列化する
開発中に生のAPIデータを確認したいですか?JSON.stringify() を使用してください。オブジェクトを直接渡しても動作しません。
// ❌ 間違い — オブジェクト全体をレンダリングしようとする
return ```
{data}
```;
// ✅ 修正済み — 読みやすくインデントされたJSON文字列
return ```
{JSON.stringify(data, null, 2)}
```;
本番環境にリリースする前にこれを削除してください — デバッグツールであり、UIではありません。
修正5 — コレクションを正しくmapする
Object.entries() は [key, value] ペアの配列を返します。その配列は自動的にレンダリングできるわけではありません — .map() でJSXエレメントに変換する必要があります。
// ❌ 間違い — Object.entriesは配列の配列を返すだけでJSXではない
const settings = { theme: 'dark', lang: 'en' };
return {Object.entries(settings)};
// ✅ 修正済み — 各エントリをリストアイテムにする
return (
{Object.entries(settings).map(([key, value]) => (
- {key}: {value}
))}
);
修正6 — 不明な値の型をガードする
特にサードパーティAPIを使用する場合、値がどのような形で届くかを制御できないことがあります。レンダリング前に型チェックを追加しましょう。
// ❌ 間違い — responseが文字列でなくオブジェクトだとクラッシュする
return {response}
;
// ✅ 安全 — 文字列または数値の場合のみレンダリングする
return {typeof response === 'string' ? response : null}
;
確認
修正後、以下の3点を確認してください:
- ブラウザのエラーオーバーレイが消えている
- コンポーネントがクラッシュせずにレンダリングされる
- テストスイートが通過する:
npm testまたはpnpm test
エラーが続く場合、オブジェクトはコンポーネントツリーの別の場所から来ている可能性があります。エラーメッセージに表示されているキーを追っていけば、そこにたどり着けます。
予防のヒント
TypeScriptはこれをコンパイル時に検出します。 プロパティを string として型定義すれば、TypeScriptが誤って User オブジェクトを渡すことを防いでくれます。これにより、ブラウザで問題が発生する前に、このクラスのバグ全体を排除できます。
interface Props {
name: string; // 呼び出し元は文字列を渡す必要があり、オブジェクトは不可
}
function Label({ name }: Props) {
return {name};
}
APIレスポンスは境界でデストラクチャリングしましょう。 データがアプリに入ってくる箇所で、実際に必要なフィールドだけを取り出してください — コンポーネントに広がる前に。深くネストされた未知のJSONは、レスポンスを JSON Formatter & Validator に貼り付けて構造を可視化し、どのキーを対象にすべきか確認するとよいでしょう。すべてブラウザ上で動作し、データがアップロードされることはありません。
TypeScriptを使用していない場合は、PropTypesで軽量なランタイムバリデーションを使用しましょう:
import PropTypes from 'prop-types';
UserCard.propTypes = {
user: PropTypes.shape({
name: PropTypes.string.isRequired,
}).isRequired,
};

