From 64739f83991205333867000d7813e82b0d98f871 Mon Sep 17 00:00:00 2001 From: shadcn Date: Tue, 29 Oct 2024 21:30:05 +0400 Subject: [PATCH] feat: react 19 (#5621) * feat(shadcn): add flag prompt for npm * docs: add docs for react 19 * chore: add changeset * test: update snapshots * docs: add notes for recharts * docs: fix * fix * fix: linting --- .changeset/breezy-garlics-whisper.md | 5 + apps/www/components/callout.tsx | 17 +- apps/www/components/mdx-components.tsx | 13 +- apps/www/config/docs.ts | 5 + apps/www/content/docs/components/chart.mdx | 8 +- apps/www/content/docs/installation/next.mdx | 6 + apps/www/content/docs/react-19.mdx | 166 ++++++++++++++++++ apps/www/next.config.mjs | 5 + .../src/utils/updaters/update-dependencies.ts | 46 ++++- .../registry-resolve-items-tree.test.ts.snap | 2 +- 10 files changed, 257 insertions(+), 16 deletions(-) create mode 100644 .changeset/breezy-garlics-whisper.md create mode 100644 apps/www/content/docs/react-19.mdx diff --git a/.changeset/breezy-garlics-whisper.md b/.changeset/breezy-garlics-whisper.md new file mode 100644 index 00000000000..47baf5c1d38 --- /dev/null +++ b/.changeset/breezy-garlics-whisper.md @@ -0,0 +1,5 @@ +--- +"shadcn": patch +--- + +add flag prompt for npm diff --git a/apps/www/components/callout.tsx b/apps/www/components/callout.tsx index 848d8717b21..34a9f23e72b 100644 --- a/apps/www/components/callout.tsx +++ b/apps/www/components/callout.tsx @@ -1,18 +1,19 @@ +import { cn } from "@/lib/utils" import { Alert, AlertDescription, AlertTitle, } from "@/registry/new-york/ui/alert" -interface CalloutProps { - icon?: string - title?: string - children?: React.ReactNode -} - -export function Callout({ title, children, icon, ...props }: CalloutProps) { +export function Callout({ + title, + children, + icon, + className, + ...props +}: React.ComponentProps & { icon?: string }) { return ( - + {icon && {icon}} {title && {title}} {children} diff --git a/apps/www/components/mdx-components.tsx b/apps/www/components/mdx-components.tsx index c64cf6dd906..73fc1b39264 100644 --- a/apps/www/components/mdx-components.tsx +++ b/apps/www/components/mdx-components.tsx @@ -139,10 +139,10 @@ const components = {
), table: ({ className, ...props }: React.HTMLAttributes) => ( -
+
), tr: ({ className, ...props }: React.HTMLAttributes) => ( - + ), th: ({ className, ...props }: React.HTMLAttributes) => (
) => ( + +**Note:** If you are using charts with **React 19** or the **Next.js 15**, see the note [here](/docs/react-19#recharts). + + + -**Note:** If you are trying to use charts with **React 19** or the **Next.js 15**, you will need the [recharts@alpha](https://github.com/recharts/recharts/releases/tag/v2.13.0-alpha.4) release currently. +**Note:** If you are using charts with **React 19** or the **Next.js 15**, see the note [here](/docs/react-19#recharts). diff --git a/apps/www/content/docs/installation/next.mdx b/apps/www/content/docs/installation/next.mdx index e9923e2541b..0aba8a171db 100644 --- a/apps/www/content/docs/installation/next.mdx +++ b/apps/www/content/docs/installation/next.mdx @@ -3,6 +3,12 @@ title: Next.js description: Install and configure Next.js. --- + + +**If you're using Next.js 15, see the [Next.js 15 + React 19](/docs/installation/react-19) guide.** + + + ### Create project diff --git a/apps/www/content/docs/react-19.mdx b/apps/www/content/docs/react-19.mdx new file mode 100644 index 00000000000..d82854b360e --- /dev/null +++ b/apps/www/content/docs/react-19.mdx @@ -0,0 +1,166 @@ +--- +title: Next.js 15 + React 19 +description: Using shadcn/ui with Next.js 15 and React 19. +--- + + + **The following guide applies to any framework that supports React 19**. I + titled this page "Next.js 15 + React 19" to help people upgrading to Next.js + 15 find it. We are working with package maintainers to help upgrade to React + 19. + + +## TL;DR + +If you're using `npm`, you can install shadcn/ui dependencies with a flag. The `shadcn` CLI will prompt you to select a flag when you run it. No flags required for pnpm, bun, or yarn. + +See [Upgrade Status](#upgrade-status) for the status of React 19 support for each package. + +## What's happening? + +React 19 is now [rc](https://www.npmjs.com/package/react?activeTab=versions) and is [tested and supported in the latest Next.js 15 release](https://nextjs.org/blog/next-15#react-19). + +To support React 19, package maintainers will need to test and update their packages to include React 19 as a peer dependency. This is [already](https://github.com/radix-ui/primitives/pull/2952) [in](https://github.com/pacocoursey/cmdk/pull/318) [progress](https://github.com/emilkowalski/vaul/pull/498). + +```diff /^19.0.0/ +"peerDependencies": { +- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0", ++ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0" +}, +``` + + + You can check if a package lists React 19 as a peer dependency by running + `npm info peerDependencies`. + + +In the meantime, if you are installing a package that **does not** list React 19 as a peer dependency, you will see an error message like this: + +```bash +npm error code ERESOLVE +npm error ERESOLVE unable to resolve dependency tree +npm error +npm error While resolving: my-app@0.1.0 +npm error Found: react@19.0.0-rc-69d4b800-20241021 +npm error node_modules/react +npm error react@"19.0.0-rc-69d4b800-20241021" from the root project +``` + + + **Note:** This is npm only. PNPM and Bun will only show a silent warning. + + +## How to fix this + +### Solution 1: `--force` or `--legacy-peer-deps` + +You can force install a package with the `--force` or the `--legacy-peer-deps` flag. + +```bash +npm i --force + +npm i --legacy-peer-deps +``` + +This will install the package and ignore the peer dependency warnings. + + + + + What do the `--force` and `--legacy-peer-deps` flag do? + + + + - `--force`: Ignores and overrides any dependency conflicts, forcing the + installation of packages. + - `--legacy-peer-deps`: Skips strict peer dependency checks, allowing + installation of packages with unmet peer dependencies to avoid errors. + + + + + + +### Solution 2: Use React 18 + +You can downgrade `react` and `react-dom` to version 18, which is compatible with the package you are installing and upgrade when the dependency is updated. + +```bash +npm i react@18 react-dom@18 +``` + +Whichever solution you choose, make sure you test your app thoroughly to ensure +there are no regressions. + +## Using shadcn/ui on Next.js 15 + +### Using pnpm, bun, or yarn + +Follow the instructions in the [installation guide](/docs/installation/next) to install shadcn/ui. No flags are needed. + +### Using npm + +When you run `npx shadcn@latest init -d`, you will be prompted to select an option to resolve the peer dependency issues. + +```bash +It looks like you are using React 19. +Some packages may fail to install due to peer dependency issues (see https://ui.shadcn.com/react-19). + +? How would you like to proceed? › - Use arrow-keys. Return to submit. +❯ Use --force + Use --legacy-peer-deps +``` + +You can then run the command with the flag you choose. + +## Adding components + +The process for adding components is the same as above. Select a flag to resolve the peer dependency issues. + +**Remember to always test your app after installing new dependencies.** + +## Upgrade Status + +To make it easy for you track the progress of the upgrade, I've created a table below with React 19 support status for the shadcn/ui dependencies. + +- ✅ - Works with React 19 using npm, pnpm, and bun. +- 🚧 - Works with React 19 using pnpm and bun. Requires flag for npm. PR is in progress. + +| Package | Status | Note | +| ---------------------------------------------------------------------------------- | ------ | ----------------------------------------------------------- | +| [radix-ui](https://www.npmjs.com/package/@radix-ui/react-icons) | ✅ | | +| [lucide-react](https://www.npmjs.com/package/lucide-react) | ✅ | | +| [class-variance-authority](https://www.npmjs.com/package/class-variance-authority) | ✅ | Does not list React 19 as a peer dependency. | +| [tailwindcss-animate](https://www.npmjs.com/package/tailwindcss-animate) | ✅ | Does not list React 19 as a peer dependency. | +| [embla-carousel-react](https://www.npmjs.com/package/embla-carousel-react) | ✅ | | +| [recharts](https://www.npmjs.com/package/recharts) | ✅ | See note [below](#recharts) | +| [react-hook-form](https://www.npmjs.com/package/react-hook-form) | ✅ | | +| [react-resizable-panels](https://www.npmjs.com/package/react-resizable-panels) | ✅ | | +| [sonner](https://www.npmjs.com/package/sonner) | ✅ | | +| [react-day-picker](https://www.npmjs.com/package/react-day-picker) | ✅ | Works with flag for npm. Work to upgrade to v9 in progress. | +| [input-otp](https://www.npmjs.com/package/input-otp) | ✅ | | +| [vaul](https://www.npmjs.com/package/vaul) | ✅ | | +| [@radix-ui/react-icons](https://www.npmjs.com/package/@radix-ui/react-icons) | 🚧 | See [PR #184](https://github.com/radix-ui/icons/pull/184) | +| [cmdk](https://www.npmjs.com/package/cmdk) | 🚧 | See [PR #318](https://github.com/pacocoursey/cmdk/pull/318) | + +If you have any questions, please [open an issue](https://github.com/shadcn/ui/issues) on GitHub. + +## Recharts + +To use recharts with React 19, you will need to override the `react-is` dependency. + + + +Add the following to your `package.json` + +```json title="package.json" +"overrides": { + "react-is": "^19.0.0-rc-69d4b800-20241021" +} +``` + +Note: the `react-is` version needs to match the version of React 19 you are using. The above is an example. + +Run `npm install --legacy-peer-deps` + + diff --git a/apps/www/next.config.mjs b/apps/www/next.config.mjs index 02f9ee7aa12..299da42690e 100644 --- a/apps/www/next.config.mjs +++ b/apps/www/next.config.mjs @@ -58,6 +58,11 @@ const nextConfig = { destination: "/docs/components/sidebar", permanent: true, }, + { + source: "/react-19", + destination: "/docs/react-19", + permanent: true, + }, ] }, } diff --git a/packages/shadcn/src/utils/updaters/update-dependencies.ts b/packages/shadcn/src/utils/updaters/update-dependencies.ts index 263365f0ba7..ad3c1af5abd 100644 --- a/packages/shadcn/src/utils/updaters/update-dependencies.ts +++ b/packages/shadcn/src/utils/updaters/update-dependencies.ts @@ -1,8 +1,11 @@ import { Config } from "@/src/utils/get-config" +import { getPackageInfo } from "@/src/utils/get-package-info" import { getPackageManager } from "@/src/utils/get-package-manager" +import { logger } from "@/src/utils/logger" import { RegistryItem } from "@/src/utils/registry/schema" import { spinner } from "@/src/utils/spinner" import { execa } from "execa" +import prompts from "prompts" export async function updateDependencies( dependencies: RegistryItem["dependencies"], @@ -25,12 +28,53 @@ export async function updateDependencies( silent: options.silent, })?.start() const packageManager = await getPackageManager(config.resolvedPaths.cwd) + + // Offer to use --force or --legacy-peer-deps if using React 19 with npm. + let flag = "" + if (isUsingReact19(config) && packageManager === "npm") { + dependenciesSpinner.stopAndPersist() + logger.warn( + "\nIt looks like you are using React 19. \nSome packages may fail to install due to peer dependency issues (see https://ui.shadcn.com/react-19).\n" + ) + const confirmation = await prompts([ + { + type: "select", + name: "flag", + message: "How would you like to proceed?", + choices: [ + { title: "Use --force", value: "force" }, + { title: "Use --legacy-peer-deps", value: "legacy-peer-deps" }, + ], + }, + ]) + + if (confirmation) { + flag = confirmation.flag + } + } + + dependenciesSpinner?.start() + await execa( packageManager, - [packageManager === "npm" ? "install" : "add", ...dependencies], + [ + packageManager === "npm" ? "install" : "add", + ...(packageManager === "npm" && flag ? [`--${flag}`] : []), + ...dependencies, + ], { cwd: config.resolvedPaths.cwd, } ) dependenciesSpinner?.succeed() } + +function isUsingReact19(config: Config) { + const packageInfo = getPackageInfo(config.resolvedPaths.cwd) + + if (!packageInfo?.dependencies?.react) { + return false + } + + return /^(?:\^|~)?19(?:\.\d+)*(?:-.*)?$/.test(packageInfo.dependencies.react) +} diff --git a/packages/shadcn/test/utils/schema/__snapshots__/registry-resolve-items-tree.test.ts.snap b/packages/shadcn/test/utils/schema/__snapshots__/registry-resolve-items-tree.test.ts.snap index e83b429fe1a..308c60901f9 100644 --- a/packages/shadcn/test/utils/schema/__snapshots__/registry-resolve-items-tree.test.ts.snap +++ b/packages/shadcn/test/utils/schema/__snapshots__/registry-resolve-items-tree.test.ts.snap @@ -384,7 +384,7 @@ const CommandItem = React.forwardRef<