Next.js 13项目引入Tailwind UI组件时出现JSON解析错误
问题:Next.js 13 + TypeScript + Tailwind UI 嵌套依赖JSON解析错误
我正在开发个人Next.js 13 + TypeScript + Tailwind CSS项目,为快速搭建原型尝试使用Tailwind UI组件,但始终在嵌套依赖中遇到JSON解析错误:

我创建了全新空白Next.js项目,仅使用Tailwind UI的堆叠式App Shell组件(已标记为客户端组件"use client"),以排除自定义文件影响。
AppShell组件代码
"use client" import { Fragment } from 'react' import { Disclosure, Menu, Transition } from '@headlessui/react' import { Bars3Icon, BellIcon, XMarkIcon } from '@heroicons/react/24/outline' const user = { name: 'Tom Cook', email: 'tom@example.com', imageUrl: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', } const navigation = [ { name: 'Dashboard', href: '#', current: true }, { name: 'Team', href: '#', current: false }, { name: 'Projects', href: '#', current: false }, { name: 'Calendar', href: '#', current: false }, { name: 'Reports', href: '#', current: false }, ] const userNavigation = [ { name: 'Your Profile', href: '#' }, { name: 'Settings', href: '#' }, { name: 'Sign out', href: '#' }, ] function classNames(...classes) { return classes.filter(Boolean).join(' ') } export default function AppShell() { return ( <> {/* This example requires updating your template: ``` <html class="h-full bg-gray-100"> <body class="h-full"> ``` */} <div className="min-h-full"> <Disclosure as="nav" className="bg-gray-800"> {({ open }) => ( <> <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> <div className="flex h-16 items-center justify-between"> <div className="flex items-center"> <div className="flex-shrink-0"> <img className="h-8 w-8" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company" /> </div> <div className="hidden md:block"> <div className="ml-10 flex items-baseline space-x-4"> {navigation.map((item) => ( <a key={item.name} href={item.href} className={classNames( item.current ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white', 'rounded-md px-3 py-2 text-sm font-medium' )} aria-current={item.current ? 'page' : undefined} > {item.name} </a> ))} </div> </div> </div> <div className="hidden md:block"> <div className="ml-4 flex items-center md:ml-6"> <button type="button" className="rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" > <span className="sr-only">View notifications</span> <BellIcon className="h-6 w-6" aria-hidden="true" /> </button> {/* Profile dropdown */} <Menu as="div" className="relative ml-3"> <div> <Menu.Button className="flex max-w-xs items-center rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800"> <span className="sr-only">Open user menu</span> <img className="h-8 w-8 rounded-full" src={user.imageUrl} alt="" /> </Menu.Button> </div> <Transition as={Fragment} enter="transition ease-out duration-100" enterFrom="transform opacity-0 scale-95" enterTo="transform opacity-100 scale-100" leave="transition ease-in duration-75" leaveFrom="transform opacity-100 scale-100" leaveTo="transform opacity-0 scale-95" > <Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"> {userNavigation.map((item) => ( <Menu.Item key={item.name}> {({ active }) => ( <a href={item.href} className={classNames( active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700' )} > {item.name} </a> )} </Menu.Item> ))} </Menu.Items> </Transition> </Menu> </div> </div> <div className="-mr-2 flex md:hidden"> {/* Mobile menu button */} <Disclosure.Button className="inline-flex items-center justify-center rounded-md bg-gray-800 p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800"> <span className="sr-only">Open main menu</span> {open ? ( <XMarkIcon className="block h-6 w-6" aria-hidden="true" /> ) : ( <Bars3Icon className="block h-6 w-6" aria-hidden="true" /> )} </Disclosure.Button> </div> </div> </div> <Disclosure.Panel className="md:hidden"> <div className="space-y-1 px-2 pb-3 pt-2 sm:px-3"> {navigation.map((item) => ( <Disclosure.Button key={item.name} as="a" href={item.href} className={classNames( item.current ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white', 'block rounded-md px-3 py-2 text-base font-medium' )} aria-current={item.current ? 'page' : undefined} > {item.name} </Disclosure.Button> ))} </div> <div className="border-t border-gray-700 pb-3 pt-4"> <div className="flex items-center px-5"> <div className="flex-shrink-0"> <img className="h-10 w-10 rounded-full" src={user.imageUrl} alt="" /> </div> <div className="ml-3"> <div className="text-base font-medium leading-none text-white">{user.name}</div> <div className="text-sm font-medium leading-none text-gray-400">{user.email}</div> </div> <button type="button" className="ml-auto flex-shrink-0 rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" > <span className="sr-only">View notifications</span> <BellIcon className="h-6 w-6" aria-hidden="true" /> </button> </div> <div className="mt-3 space-y-1 px-2"> {userNavigation.map((item) => ( <Disclosure.Button key={item.name} as="a" href={item.href} className="block rounded-md px-3 py-2 text-base font-medium text-gray-400 hover:bg-gray-700 hover:text-white" > {item.name} </Disclosure.Button> ))} </div> </div> </Disclosure.Panel> </> )} </Disclosure> <header className="bg-white shadow"> <div className="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8"> <h1 className="text-3xl font-bold tracking-tight text-gray-900">Dashboard</h1> </div> </header> <main> <div className="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">{/* Your content */}</div> </main> </div> </> ) }
根目录page.ts代码
import { Inter } from "next/font/google"; import AppShell from "./components/AppShell"; const inter = Inter({ subsets: ["latin"] }); export default function Home() { return ( <main className="flex min-h-screen flex-col items-center justify-between p-24"> <div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex"> <AppShell></AppShell> </div> </main> ); }
我已安装所有依赖并完成配置,请问该问题是否与客户端组件和服务端组件的区分有关?
解决方案
- 检查依赖版本兼容性:这个错误通常和
@headlessui/react、@heroicons/react与Next.js 13 App Router的兼容性有关。确保安装的是适配App Router的版本,比如@headlessui/reactv1.7+,@heroicons/reactv2.0+。执行以下命令更新依赖:
npm update @headlessui/react @heroicons/react
确认客户端组件标记规范:
"use client"指令必须是组件文件的第一行,不能有任何注释或空格在它之前。你的AppShell组件这部分是合规的,但可再次检查无语法错误。验证TypeScript配置:Next.js 13 App Router对
tsconfig.json有特定要求,确保配置包含以下关键项:
{ "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } ], "paths": { "@/*": ["./*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] }
- 清理缓存并重启服务:Next.js的缓存可能导致依赖解析异常,执行以下命令清理后重启开发服务器:
rm -rf .next node_modules package-lock.json npm install npm run dev
- 关于客户端/服务端组件区分:你的用法是正确的——Tailwind UI依赖的
@headlessui/react需要客户端组件环境,所以在AppShell顶部标记"use client"是必要的。这个错误本身不是组件区分错误导致的,而是依赖解析或版本兼容问题,但确保客户端组件标记正确是运行的前提。
内容的提问来源于stack exchange,提问作者Luciano Deben
相关产品推荐
相关产品推荐

