Shadcn/ui的Command组件在React 19 RC和Next.js 15中无法正常工作
Shadcn/ui的Command组件在React 19 RC和Next.js 15中无法正常工作
我刚在React 19 RC + Next.js 15的环境里踩过这个坑!shadcn/ui的Command组件默认代码是基于React 18写的,在新版本里会因为类型定义、客户端组件规则的变化出现问题。下面是我调整后可以正常运行的完整组件代码,亲测有效:
'use client'; import * as React from 'react'; import { type DialogProps } from '@radix-ui/react-dialog'; import { Command as CommandPrimitive } from 'cmdk'; import { Search } from 'lucide-react'; import { cn } from '@/app/lib/utils'; import { Dialog, DialogContent } from '@/components/ui/dialog'; // 适配React 19的forwardRef类型定义 const Command = React.forwardRef< React.ElementRef<typeof CommandPrimitive>, React.ComponentPropsWithoutRef<typeof CommandPrimitive> >(({ className, ...props }, ref) => ( <CommandPrimitive ref={ref} className={cn( 'flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground', className, )} {...props} /> )); Command.displayName = CommandPrimitive.displayName; // 补充Command的常用子组件,避免React 19下的渲染警告 const CommandInput = React.forwardRef< React.ElementRef<typeof CommandPrimitive.Input>, React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> >(({ className, ...props }, ref) => ( <div className="flex items-center border-b px-3"> <Search className="mr-2 h-4 w-4 opacity-50" /> <CommandPrimitive.Input ref={ref} className={cn( 'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', className, )} {...props} /> </div> )); CommandInput.displayName = CommandPrimitive.Input.displayName; const CommandList = React.forwardRef< React.ElementRef<typeof CommandPrimitive.List>, React.ComponentPropsWithoutRef<typeof CommandPrimitive.List> >(({ className, ...props }, ref) => ( <CommandPrimitive.List ref={ref} className={cn( 'flex-1 overflow-y-auto p-1', className, )} {...props} /> )); CommandList.displayName = CommandPrimitive.List.displayName; const CommandEmpty = React.forwardRef< React.ElementRef<typeof CommandPrimitive.Empty>, React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty> >(({ className, ...props }, ref) => ( <CommandPrimitive.Empty ref={ref} className={cn( 'py-6 text-center text-sm', className, )} {...props} /> )); CommandEmpty.displayName = CommandPrimitive.Empty.displayName; const CommandDialog = ({ children, ...props }: DialogProps) => { return ( <Dialog {...props}> <DialogContent className="overflow-hidden p-0 shadow-lg"> <Command className="h-[450px] w-[400px]"> {children} </Command> </DialogContent> </Dialog> ); }; export { Command, CommandDialog, CommandInput, CommandList, CommandEmpty };
主要做了这些调整来适配新版本:
- 确保
'use client'在文件最顶部,Next.js 15的App Router对客户端组件的标识要求更严格 - 修正了React 19下forwardRef的类型推断,把原来的转义符(
</>)换成真实的尖括号,避免TypeScript报错 - 补充了
CommandInput、CommandList、CommandEmpty这些常用子组件的封装,解决React 19因组件未正确转发ref导致的渲染警告 - 调整了
CommandDialog里DialogContent的样式,确保和组件布局匹配
你可以直接替换原来的组件代码,然后在客户端组件里这样使用:
'use client'; import { useState } from 'react'; import { CommandDialog, CommandInput, CommandList, CommandEmpty } from '@/components/ui/command'; export default function CommandDemo() { const [open, setOpen] = useState(false); return ( <div> <button onClick={() => setOpen(true)}>打开命令面板</button> <CommandDialog open={open} onOpenChange={setOpen}> <CommandInput placeholder="搜索命令..." /> <CommandList> <CommandEmpty>未找到匹配的命令</CommandEmpty> {/* 这里添加你的命令选项 */} </CommandList> </CommandDialog> </div> ); }
备注:内容来源于stack exchange,提问作者moth




