Next.jsで"Element type is invalid"エラーが出た時の解決策
Next.jsで開発中に、以下のようなエラーに遭遇することがあります。
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
このエラーは、コンポーネントのインポートやエクスポートが正しく行われていない場合に発生することが多いですが、今回はNext.jsのサーバーコンポーネントとクライアントコンポーネントの扱いに起因する問題でした。
この記事では、このエラーが発生した原因と、その解決策について解説します。
エラーの原因:サーバーコンポーネント内でのクライアントサイド機能の使用
Next.jsのApp Routerでは、コンポーネントはデフォルトでサーバーコンポーネントとして扱われます。サーバーコンポーネントはサーバー側でレンダリングされるため、状態管理 (useState) やイベントリスナー (onClickなど)、そして今回問題となったアニメーションライブラリ (motion) のようなブラウザ(クライアントサイド)でしか動作しない機能を使うことができません。
今回エラーが発生したsrc/app/blogs/page.tsxはサーバーコンポーネントでしたが、その中でmotion.liというアニメーション用のコンポーネントを直接使用していました。これが「不正な要素タイプ(Element type is invalid)」というエラーを引き起こしていました。
解決策:クライアントコンポーネントへの分離
この問題を解決するために、クライアントサイドで動作する必要がある部分を別のコンポーネントとして切り出し、それをクライアントコンポーネントとしてマークします。
具体的な手順は以下の通りです。
1. クライアントコンポーネントの作成
まず、アニメーション処理(motion.li)を含むリスト部分を、BlogList.tsxのような新しいファイルに切り出します。
そして、この新しいファイルの先頭に'use client'という記述を追加します。これにより、このコンポーネントはクライアントコンポーネントとして扱われるようになります。
'use client'
import Link from 'next/link'
import { motion } from 'motion/react'
import BlogCard from '@/components/ui/BlogCard'
import { allBlogs } from 'contentlayer2/generated'
interface BlogListProps {
blogs: typeof allBlogs
}
export default function BlogList({ blogs }: BlogListProps) {
return (
<ul className="flex flex-col mb-12">
{blogs.map((b, index) => (
<motion.li
key={b.slug}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: index * 0.2 }}
viewport={{ once: true }}
>
<Link href={`/blogs/${b.slug}`}>
<BlogCard title={b.title} date={b.date} categories={b.categories} />
</Link>
</motion.li>
))}
</ul>
)
}2. サーバーコンポーネントからクライアントコンポーネントを呼び出す
次に、元のsrc/app/blogs/page.tsx(サーバーコンポーネント)から、先ほど作成したBlogList.tsx(クライアントコンポーネント)を呼び出すように変更します。
import { allBlogs } from 'contentlayer2/generated'
import { notFound } from 'next/navigation'
import Sidebar from '@/components/mdx/Sidebar'
import { Archivo_Black } from 'next/font/google'
import BlogList from './BlogList' // 作成したクライアントコンポーネントをインポート
// ... (省略)
export default async function BlogsPage() {
const blogs = allBlogs
.filter((b) => !b.draft)
.sort((a, b) => +new Date(b.date) - +new Date(a.date))
if (!blogs) return notFound()
return (
<div className='tablet:pb-70 pb-20'>
{/* ... (省略) */}
<div className="flex gap-20 max-w-7xl mx-auto px-6">
<Sidebar />
<div className='w-full'>
<h1 className="text-2xl font-bold mb-8">すべてのお知らせ</h1>
{/* クライアントコンポーネントを呼び出す */}
<BlogList blogs={blogs} />
</div>
</div>
</div>
)
}まとめ
Next.jsで"Element type is invalid"というエラーが出た場合、コンポーネントのインポート・エクスポートの記述ミスをまず疑うべきですが、App Router環境ではサーバーコンポーネント内でクライアントサイド専用の機能を使っていないかも確認することが重要です。
サーバーコンポーネントとクライアントコンポーネントの役割を明確に分離し、'use client'を適切に使用することで、この種の問題を解決することができます。
