Reactでの「Element type is invalid: expected a string but got: undefined」エラーの修正方法

beginner⚛️ React2026-05-25| React 16.x, 17.x, 18.x / Next.js / Vite / Webpack

Error Message

React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
#react#javascript#デバッグ#フロントエンド

エラーメッセージ

真っ白な画面を前に、コンソールには赤文字のエラーが並んでいる。これは、すべてのReact開発者が通る道です。通常、エラーは次のように表示されます。

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.

ブラウザが実際に伝えていること

Reactは本質的に、「何かをレンダリングするように指示されましたが、それが何なのか全くわかりません」と言っています。<MyComponent />と記述すると、Reactはその変数を評価します。もしそれがundefinedであれば、レンダリングエンジンはクラッシュします。Reactが理解できるのは、HTMLタグ('div'や'span'など)、または実際のJavaScriptの関数やクラスのみです。

この問題のほとんどは、importexportの記述が一致していないことが原因で発生します。一方のファイルがパッケージを送信していても、もう一方がそれを正しく受信できていない状態です。

1. 原因の90%:Default Import vs. Named Imports

素早いリファクタリングの最中に、このミスが起きるのをよく目にします。コンポーネントがdefaultとしてエクスポートされている場合、インポート時に波括弧({})を使用することはできません。名前付きエクスポート(named export)の場合は、波括弧を使用する必要があります。

シナリオA:Default Exportの罠

export defaultを使用すると、ファイル全体を一つの単位として送信することになります。

誤り:

// MyComponent.js
export default function MyComponent() { return <div>Hello</div>; }

// App.js
import { MyComponent } from './MyComponent'; // エラー!ここでMyComponentはundefinedになります。

正解:

import MyComponent from './MyComponent';

シナリオB:Named Exportの不一致

名前付きエクスポートでは、一つのファイルから複数のものをエクスポートできますが、正確さが求められます。

誤り:

// MyComponent.js
export const MyComponent = () => <div>Hello</div>;

// App.js
import MyComponent from './MyComponent'; // エラー!存在しないデフォルトエクスポートを探しています。

正解:

import { MyComponent } from './MyComponent';

2. 「バレルファイル」の悩み (index.js)

フォルダのインポートを整理するためにindex.jsファイルを使用するのは優れたパターンですが、壊れると厄介です。./components/Buttonではなく./componentsからインポートする場合、index.jsが完璧にマッピングされている必要があります。

次のような構造を考えてみましょう:

components/
  ├── Button.js (using export default)
  └── index.js

index.jsは次のようになっている必要があります:

export { default as Button } from './Button';

このファイルの記述が1行足りないだけで、アプリケーションはundefinedをレンダリングしようとしてしまいます。新しいUIコンポーネントを追加したときは、他の場所で使用する前に、バレルファイルに実際に追加したかどうかを再確認してください。

3. 循環参照:無限ループ

これはこのエラーの中で最も厄介なパターンです。コンポーネントAコンポーネントBをインポートし、かつコンポーネントBコンポーネントAをインポートしている場合、モジュールシステムがループの解析を完了できないため、どちらかがundefinedとしてロードされます。

ループが疑われる場合は、共有ロジックや小さなサブコンポーネントをSharedUtils.jsのような3番目のファイルに移動してください。これにより循環が断ち切られ、両方のコンポーネントが依存関係を安全にインポートできるようになります。

4. 本番環境における大文字・小文字の区別

macOSでのローカル開発は、多くの場合、大文字と小文字を区別しません。実際のファイル名がMyButton.jsであるのに./Mybuttonとしてインポートしても、自分のPCでは正常に動作するかもしれません。しかし、VercelやAWSのようなLinux環境にデプロイすると、ビルドが失敗するか、参照がundefinedになります。

常にファイル名の大文字・小文字を正確に一致させてください。これにより、「自分のマシンでは動くのに」という問題のデバッグに費やす時間を節約できます。

30秒でできる確認方法

推測に頼ってはいけません。コンポーネントが壊れる直前でconsole.logを使用してください。これが疑念を確認する最短の方法です。

import { MyComponent } from './MyComponent';

// ブラウザのコンソールでこれを確認してください
console.log("Is MyComponent defined?", MyComponent);

const App = () => {
  return <MyComponent />;
};

ログにundefinedと表示されれば、問題はJSXの構文ではなく、import/exportの橋渡しにあることが確認できます。

予防のためのプロのヒント

  • 名前付きエクスポートを徹底する: 私はexport const MyComponent...を好んで使います。なぜなら、IDEがインポートを100%正しく自動補完してくれるからです。
  • TypeScriptを採用する: TypeScriptは「保存」を押す前にこのエラーをキャッチします。ソースファイルに存在しないインポートは許可されません。
  • サードパーティのアップデートに注意する: ライブラリのメジャーバージョンアップでは、エクスポートの方法が変更されることがよくあります。最近framer-motionlucide-reactをアップデートした場合は、名前付きエクスポートがデフォルトエクスポートに変更されていないか(あるいはその逆か)、ドキュメントを確認してください。

Related Error Notes