初級9分で読める

【解決】Objects are not valid as a React child エラーの修正方法

ReactのObjects are not valid as a React childエラーを解説。オブジェクト・Date・Promiseをそのまま描画しようとした際の原因と、文字列への変換・条件分岐・プロパティ参照による解決方法を実例付きで説明します。

Error: Objects are not valid as a React child (found: object with keys {id, name}).

「オブジェクトを{}に入れて表示しようとしたら怒られた」というパターンだ。JSXの{}は「JavaScriptの式をここに埋め込む」という意味で、どんな値でも表示できるわけではない。

JSXが表示できる値・できない値

Reactがレンダリングできる値は決まっている。

表示できる 表示できない
string プレーンオブジェクト {}
number Date オブジェクト
boolean(何も表示されない) Promise
null / undefined(何も表示されない) Error オブジェクト
ReactElement Map / Set
ReactElement[](配列) RegExp

共通点がある。「表示できない」ものはすべて、どう文字として表現すべきかReactが判断できないものだ。Dateの場合も、日付を「2024/01/15」と表示するのか「2024年1月15日」と表示するのかはアプリによって違う。Reactはそれを勝手に決めない。

パターン1:オブジェクトのプロパティを参照し忘れた(最頻出)

const user = { id: 1, name: '山田太郎' }

// ❌ エラー:オブジェクトそのものを表示しようとしている
return <div>{user}</div>

// ✅ 表示したいプロパティを指定する
return <div>{user.name}</div>

APIのレスポンスをstateに入れて、そのまま描画しようとするケースが特に多い。

const [profile, setProfile] = useState(null)
// ...

// ❌ profileオブジェクトをそのまま描画
return <div>{profile}</div>

// ✅ 必要なフィールドだけ取り出す
return <div>{profile?.name}</div>

エラーメッセージの found: object with keys {id, name} を読むと、どのオブジェクトが問題かわかる。

パターン2:Dateオブジェクトをそのまま描画した

new Date() はオブジェクトなので、文字列に変換してから使う。

const today = new Date()

// ❌ エラー:DateはオブジェクトなのでNG
return <p>{today}</p>

// ✅ 文字列に変換する
return <p>{today.toLocaleDateString('ja-JP')}</p>       // "2024/1/15"
return <p>{today.toISOString().split('T')[0]}</p>       // "2024-01-15"
return <p>{today.toLocaleString('ja-JP', { year: 'numeric', month: 'long', day: 'numeric' })}</p>  // "2024年1月15日"

どのフォーマットで表示したいかによって変換方法を選ぶ。MarkdownのfrontmatterからDateが渡ってくる場合も同様だ。

パターン3:Promiseをそのまま描画した

fetch() や非同期関数の戻り値は Promise というオブジェクトだ。await なしで使うとそのままオブジェクトになる。

// ❌ fetch()はPromiseを返す。Promiseはオブジェクト
return <div>{fetch('/api/user')}</div>

Reactコンポーネント(Client Component)でデータを取得するには、useEffectとuseStateを使う。

// ✅ useEffectでデータを取得してstateに保存する
const [user, setUser] = useState<User | null>(null)

useEffect(() => {
  fetch('/api/user')
    .then(res => res.json())
    .then(data => setUser(data))
}, [])

return <div>{user?.name ?? 'ロード中...'}</div>

パターン4:条件分岐でオブジェクトを返している

&& 演算子の右側がオブジェクトになっているケース。

const config = { theme: 'dark', lang: 'ja' }

// ❌ 条件がtrueのときオブジェクトを返している
return <div>{isAdmin && config}</div>

// ✅ 表示したい値に変換してから返す
return <div>{isAdmin && config.theme}</div>

// ✅ デバッグ用途ならJSON.stringifyで確認できる
return <div>{isAdmin && JSON.stringify(config)}</div>

パターン5:Errorオブジェクトをそのまま表示した

catch で受け取った Error もオブジェクトなので、そのままでは表示できない。

const [error, setError] = useState<Error | null>(null)

// ❌ Errorオブジェクトはオブジェクト
return <div>{error}</div>

// ✅ .message プロパティを使う
return <div>{error?.message}</div>

stateの型を string で持つほうがシンプルなこともある。

const [errorMessage, setErrorMessage] = useState<string | null>(null)

try {
  // ...
} catch (e) {
  setErrorMessage(e instanceof Error ? e.message : '不明なエラー')
}

return <div>{errorMessage}</div>

パターン6:map()の中でオブジェクトを返している

配列を .map() しているコードは正しくても、返している値がオブジェクトのままだとエラーになる。

const items = [{ id: 1, label: 'リンゴ' }, { id: 2, label: 'バナナ' }]

// ❌ オブジェクトをそのまま返している
{items.map(item => <li key={item.id}>{item}</li>)}

// ✅ 表示したいプロパティを指定する
{items.map(item => <li key={item.id}>{item.label}</li>)}

オブジェクトのキーと値の一覧を表示したい場合は Object.entries() を使う。

const settings = { theme: 'dark', lang: 'ja', fontSize: 14 }

{Object.entries(settings).map(([key, value]) => (
  <li key={key}>{key}: {String(value)}</li>
))}

TypeScriptがあれば書いた時点でわかる

TypeScriptを使うと、オブジェクトをJSXに渡した時点でエラーになる。実行してから気づくより早い。

const user = { id: 1, name: '山田太郎' }

// TypeScriptのエラー: 'user' cannot be used as a JSX child.
return <div>{user}</div>

描画する値の型を string | number に限定しておくと、オブジェクトを誤って渡す心配がなくなる。

このエラーと向き合うための1つの考え方

JSXに渡せるのは「そのまま文字や数字として表せるもの」だけ。

オブジェクトはどう表示すべきかわからないから怒られる。プロパティを取り出す・文字列に変換する・別の方法で取得する——どれかで「表示できる形」にしてから渡す。