From 478e597be74a962aa53dd2a8b6638aca7bc8863d Mon Sep 17 00:00:00 2001 From: Mohsen Ghaemmaghami Date: Mon, 14 Sep 2026 15:45:44 +0300 Subject: [PATCH 1/6] feat: support React Router v8 (react-router as peer, drop react-router-dom) `react-router` was a regular dependency (`^7`), so React Router 8 consumers ended up with a second, v7 copy of the router next to their own. It is now a peer dependency (`^7.0.0 || ^8.0.0`) plus a devDependency for local type-check/build. `react-router-dom` no longer exists in React Router 8 and nothing here imported it, so it is removed from dependencies, peers and the Vite externals. Docs updated accordingly; changeset added. Co-Authored-By: Claude Fable 5.1 --- .changeset/react-router-v8-support.md | 5 +++++ AGENTS.md | 2 +- README.md | 6 ++++-- docs/consumer-setup-guide.md | 8 +++++--- packages/components/package.json | 6 ++---- packages/components/vite.config.ts | 1 - yarn.lock | 6 ++---- 7 files changed, 19 insertions(+), 15 deletions(-) create mode 100644 .changeset/react-router-v8-support.md diff --git a/.changeset/react-router-v8-support.md b/.changeset/react-router-v8-support.md new file mode 100644 index 00000000..100db7c3 --- /dev/null +++ b/.changeset/react-router-v8-support.md @@ -0,0 +1,5 @@ +--- +"@lambdacurry/forms": minor +--- + +Support React Router v8. `react-router` is now a peer dependency (`^7.0.0 || ^8.0.0`) instead of a bundled dependency, so consumers always share a single router instance with the library, and the unused `react-router-dom` dependency/peer was dropped (that package no longer exists in React Router v8). diff --git a/AGENTS.md b/AGENTS.md index 901e7159..862efd94 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,4 +81,4 @@ Quick checklist - Forms: Zod schemas, proper messages, `fetcher.Form`, show `FormMessage` errors. - Tests: per-story decorators, semantic queries, three-phase play tests; run `yarn test`. - Monorepo: no cross-package relative imports; verify `exports`, TS `paths`, Turbo outputs. -- Consumer integration: For React Router v7 setup help, reference `docs/consumer-setup-guide.md` for Vite SSR configuration (ssr.noExternal, optimizeDeps). +- Consumer integration: For React Router v7/v8 setup help, reference `docs/consumer-setup-guide.md` for Vite SSR configuration (ssr.noExternal, optimizeDeps). diff --git a/README.md b/README.md index 73da7016..9302264e 100644 --- a/README.md +++ b/README.md @@ -66,9 +66,11 @@ const MyTable = () => { - Full accessibility support (WCAG 2.1 AA) - Comprehensive test coverage -## React Router v7 Integration +## React Router v7 / v8 Integration -When using `@lambdacurry/forms` with `remix-hook-form` in a React Router v7 application, you need to configure Vite to bundle these packages together to share the router context. Without this, you may encounter the error: +`react-router` (v7 or v8) and `remix-hook-form` are peer dependencies: install them in your application so every package shares the single router instance your app provides. + +When using `@lambdacurry/forms` with `remix-hook-form` in a React Router v7 or v8 application, you need to configure Vite to bundle these packages together to share the router context. Without this, you may encounter the error: ``` Error: useHref() may be used only in the context of a component. diff --git a/docs/consumer-setup-guide.md b/docs/consumer-setup-guide.md index 39ab4269..3f5db9d1 100644 --- a/docs/consumer-setup-guide.md +++ b/docs/consumer-setup-guide.md @@ -1,10 +1,12 @@ # Consumer Setup Guide -This guide covers how to integrate `@lambdacurry/forms` with React Router v7 applications using remix-hook-form. +This guide covers how to integrate `@lambdacurry/forms` with React Router v7 or v8 applications using remix-hook-form. -## React Router v7 Vite Configuration +`react-router` (`^7 || ^8`) and `remix-hook-form` are peer dependencies of `@lambdacurry/forms`; install them in your application. The package no longer references `react-router-dom`, which was removed in React Router v8. -When using `@lambdacurry/forms` with `remix-hook-form` in a React Router v7 application, you must configure Vite to bundle these packages together. Without this configuration, forms that render conditionally (e.g., triggered by a button click) will fail with: +## React Router Vite Configuration + +When using `@lambdacurry/forms` with `remix-hook-form` in a React Router v7 or v8 application, you must configure Vite to bundle these packages together. Without this configuration, forms that render conditionally (e.g., triggered by a button click) will fail with: ``` Error: useHref() may be used only in the context of a component. diff --git a/packages/components/package.json b/packages/components/package.json index d49eb4bd..7bce69d4 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -42,8 +42,7 @@ }, "peerDependencies": { "react": "^19.0.0", - "react-router": "^7.0.0", - "react-router-dom": "^7.0.0", + "react-router": "^7.0.0 || ^8.0.0", "remix-hook-form": "7.1.0", "zod": "^3.24.1 || ^4.0.0" }, @@ -76,8 +75,6 @@ "next-themes": "^0.4.4", "react-day-picker": "^9.7.0", "react-hook-form": "^7.53.1", - "react-router": "^7.6.3", - "react-router-dom": "^7.6.3", "remix-hook-form": "7.1.0", "sonner": "^1.7.1", "tailwind-merge": "^2.5.5", @@ -94,6 +91,7 @@ "autoprefixer": "^10.4.20", "glob": "^11.0.0", "react": "^19.0.0", + "react-router": "^7.6.3", "tailwindcss": "^4.0.0", "typescript": "^5.7.2", "vite": "^6.2.2", diff --git a/packages/components/vite.config.ts b/packages/components/vite.config.ts index d53bd131..9b15422c 100644 --- a/packages/components/vite.config.ts +++ b/packages/components/vite.config.ts @@ -62,7 +62,6 @@ export default defineConfig({ '@radix-ui/react-switch', '@radix-ui/react-tooltip', 'react-router', - 'react-router-dom', '@react-router/node', 'class-variance-authority', 'clsx', diff --git a/yarn.lock b/yarn.lock index 8a0053a1..14e8b0df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1754,7 +1754,6 @@ __metadata: react-day-picker: "npm:^9.7.0" react-hook-form: "npm:^7.53.1" react-router: "npm:^7.6.3" - react-router-dom: "npm:^7.6.3" remix-hook-form: "npm:7.1.0" sonner: "npm:^1.7.1" tailwind-merge: "npm:^2.5.5" @@ -1767,8 +1766,7 @@ __metadata: zod: "npm:^3.24.1" peerDependencies: react: ^19.0.0 - react-router: ^7.0.0 - react-router-dom: ^7.0.0 + react-router: ^7.0.0 || ^8.0.0 remix-hook-form: 7.1.0 zod: ^3.24.1 || ^4.0.0 languageName: unknown @@ -10410,7 +10408,7 @@ __metadata: languageName: node linkType: hard -"react-router-dom@npm:^7.6.2, react-router-dom@npm:^7.6.3": +"react-router-dom@npm:^7.6.2": version: 7.9.1 resolution: "react-router-dom@npm:7.9.1" dependencies: From 43887ce8630ece1d33d50e1ddd566a07bab048fb Mon Sep 17 00:00:00 2001 From: Mohsen Ghaemmaghami Date: Mon, 14 Sep 2026 16:04:46 +0300 Subject: [PATCH 2/6] chore: format scripts/release-if-needed.mjs with Biome Root `biome check .` (the PR quality-checks lint job) was already failing on main because of this file's formatting; fix it so the check can pass. Co-Authored-By: Claude Fable 5.1 --- scripts/release-if-needed.mjs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/scripts/release-if-needed.mjs b/scripts/release-if-needed.mjs index 0deaafa8..a70bf422 100644 --- a/scripts/release-if-needed.mjs +++ b/scripts/release-if-needed.mjs @@ -1,17 +1,16 @@ -import { execFileSync } from "node:child_process"; -import { readFileSync } from "node:fs"; +import { execFileSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; -const publishablePackages = ["packages/components/package.json"]; +const publishablePackages = ['packages/components/package.json']; const unpublishedPackages = publishablePackages.filter((packagePath) => { - const localPackage = JSON.parse(readFileSync(packagePath, "utf8")); + const localPackage = JSON.parse(readFileSync(packagePath, 'utf8')); try { - const exactVersion = execFileSync( - "npm", - ["view", `${localPackage.name}@${localPackage.version}`, "version"], - { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }, - ).trim(); + const exactVersion = execFileSync('npm', ['view', `${localPackage.name}@${localPackage.version}`, 'version'], { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], + }).trim(); return exactVersion !== localPackage.version; } catch { @@ -20,8 +19,8 @@ const unpublishedPackages = publishablePackages.filter((packagePath) => { }); if (unpublishedPackages.length === 0) { - console.log("All publishable package versions are already on npm."); + console.log('All publishable package versions are already on npm.'); process.exit(0); } -execFileSync("yarn", ["changeset", "publish"], { stdio: "inherit" }); +execFileSync('yarn', ['changeset', 'publish'], { stdio: 'inherit' }); From 9db9dfd4d67da6e75effcbf59ff741c8f82df816 Mon Sep 17 00:00:00 2001 From: Mohsen Ghaemmaghami Date: Mon, 14 Sep 2026 17:12:02 +0300 Subject: [PATCH 3/6] docs: address review on React Router v8 support - Changeset no longer claims a peer dependency alone guarantees a single runtime router instance; it points at the Vite dedupe/noExternal setup. - Document that React Router v8 requires Node 22.22+ in the consuming app. - Vite examples: `dedupe` belongs under `resolve`, not `optimizeDeps`. Co-Authored-By: Claude Fable 5.1 --- .changeset/react-router-v8-support.md | 2 +- README.md | 8 +++++--- docs/consumer-setup-guide.md | 12 +++++++----- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.changeset/react-router-v8-support.md b/.changeset/react-router-v8-support.md index 100db7c3..d409f5e3 100644 --- a/.changeset/react-router-v8-support.md +++ b/.changeset/react-router-v8-support.md @@ -2,4 +2,4 @@ "@lambdacurry/forms": minor --- -Support React Router v8. `react-router` is now a peer dependency (`^7.0.0 || ^8.0.0`) instead of a bundled dependency, so consumers always share a single router instance with the library, and the unused `react-router-dom` dependency/peer was dropped (that package no longer exists in React Router v8). +Support React Router v8. `react-router` is now a peer dependency (`^7.0.0 || ^8.0.0`) instead of a bundled dependency, so the library resolves the router your application installs (keep the Vite `resolve.dedupe` / `ssr.noExternal` setup from the consumer guide so only one runtime instance is bundled), and the unused `react-router-dom` dependency/peer was dropped (that package no longer exists in React Router v8). React Router v8 requires Node 22.22 or newer. diff --git a/README.md b/README.md index 9302264e..b7f313a6 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ const MyTable = () => { ## React Router v7 / v8 Integration -`react-router` (v7 or v8) and `remix-hook-form` are peer dependencies: install them in your application so every package shares the single router instance your app provides. +`react-router` (v7 or v8) and `remix-hook-form` are peer dependencies: install them in your application so the library resolves the router your app provides. React Router v8 itself requires Node 22.22 or newer in the consuming application. When using `@lambdacurry/forms` with `remix-hook-form` in a React Router v7 or v8 application, you need to configure Vite to bundle these packages together to share the router context. Without this, you may encounter the error: @@ -84,9 +84,11 @@ export default defineConfig({ ssr: { noExternal: ['react-hook-form', 'remix-hook-form', '@lambdacurry/forms'] }, - optimizeDeps: { - include: ['react', 'react-dom', 'react-router', 'react-hook-form', 'remix-hook-form'], + resolve: { dedupe: ['react', 'react-dom', 'react-router', 'react-hook-form', 'remix-hook-form'] + }, + optimizeDeps: { + include: ['react', 'react-dom', 'react-router', 'react-hook-form', 'remix-hook-form'] } }); ``` diff --git a/docs/consumer-setup-guide.md b/docs/consumer-setup-guide.md index 3f5db9d1..a0c4c738 100644 --- a/docs/consumer-setup-guide.md +++ b/docs/consumer-setup-guide.md @@ -2,7 +2,7 @@ This guide covers how to integrate `@lambdacurry/forms` with React Router v7 or v8 applications using remix-hook-form. -`react-router` (`^7 || ^8`) and `remix-hook-form` are peer dependencies of `@lambdacurry/forms`; install them in your application. The package no longer references `react-router-dom`, which was removed in React Router v8. +`react-router` (`^7 || ^8`) and `remix-hook-form` are peer dependencies of `@lambdacurry/forms`; install them in your application. The package no longer references `react-router-dom`, which was removed in React Router v8. React Router v8 requires Node 22.22 or newer. ## React Router Vite Configuration @@ -34,11 +34,13 @@ export default defineConfig({ // CRITICAL: Bundle these packages with the app to share react-router context noExternal: ['react-hook-form', 'remix-hook-form', '@lambdacurry/forms'] }, - optimizeDeps: { - // Pre-bundle dependencies to avoid runtime context issues - include: ['react', 'react-dom', 'react-router', 'react-hook-form', 'remix-hook-form'], + resolve: { // Ensure single instances of these packages dedupe: ['react', 'react-dom', 'react-router', 'react-hook-form', 'remix-hook-form'] + }, + optimizeDeps: { + // Pre-bundle dependencies to avoid runtime context issues + include: ['react', 'react-dom', 'react-router', 'react-hook-form', 'remix-hook-form'] } }); ``` @@ -49,7 +51,7 @@ export default defineConfig({ |---------|---------| | `ssr.noExternal` | Forces Vite to bundle `remix-hook-form`, `react-hook-form`, and `@lambdacurry/forms` with the application instead of treating them as external dependencies. This ensures they share the same `react-router` instance. | | `optimizeDeps.include` | Pre-bundles these packages during dev, avoiding lazy loading that can cause context issues. | -| `optimizeDeps.dedupe` | Ensures only one copy of each package exists, preventing multiple React or react-router instances. | +| `resolve.dedupe` | Ensures only one copy of each package exists, preventing multiple React or react-router instances. | ## Recommended Form Pattern From 76863fbe2074d19dea8a992c4a468c9a15992a60 Mon Sep 17 00:00:00 2001 From: Mohsen Ghaemmaghami Date: Thu, 17 Sep 2026 12:37:51 +0300 Subject: [PATCH 4/6] docs: point the troubleshooting steps at resolve.dedupe The consumer setup guide's troubleshooting section still told readers to add `optimizeDeps.dedupe`, while the configuration above it (and Vite) use `resolve.dedupe`. Align both troubleshooting entries with the documented configuration. Co-Authored-By: Claude Fable 5.1 --- docs/consumer-setup-guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/consumer-setup-guide.md b/docs/consumer-setup-guide.md index a0c4c738..5d3c5638 100644 --- a/docs/consumer-setup-guide.md +++ b/docs/consumer-setup-guide.md @@ -130,7 +130,7 @@ function MyForm({ onSuccess }: { onSuccess: () => void }) { **Cause**: Vite is treating `remix-hook-form` or `react-hook-form` as external dependencies, causing them to load with a separate `react-router` instance. -**Solution**: Add the `ssr.noExternal` and `optimizeDeps` configuration shown above. +**Solution**: Add the `ssr.noExternal`, `resolve.dedupe` and `optimizeDeps.include` configuration shown above. ### Form works on initial render but fails when opened dynamically @@ -142,7 +142,7 @@ function MyForm({ onSuccess }: { onSuccess: () => void }) { **Cause**: Dependencies are being duplicated in the bundle. -**Solution**: Add `optimizeDeps.dedupe` with React and related packages. +**Solution**: Add `resolve.dedupe` with React and related packages, as in the configuration above. ## Related Documentation From f01d8d99541fce97a86624273081e6d3695ff57d Mon Sep 17 00:00:00 2001 From: Mohsen Ghaemmaghami Date: Thu, 17 Sep 2026 13:09:56 +0300 Subject: [PATCH 5/6] docs: list every peer dependency consumers must install The setup guide and README only named react-router and remix-hook-form, but the package also declares react and zod as peer dependencies. Co-Authored-By: Claude Fable 5.1 --- README.md | 2 +- docs/consumer-setup-guide.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b7f313a6..830bf97c 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ const MyTable = () => { ## React Router v7 / v8 Integration -`react-router` (v7 or v8) and `remix-hook-form` are peer dependencies: install them in your application so the library resolves the router your app provides. React Router v8 itself requires Node 22.22 or newer in the consuming application. +`react`, `react-router` (v7 or v8), `remix-hook-form` and `zod` are peer dependencies: install them in your application so the library resolves the router (and React) your app provides. React Router v8 itself requires Node 22.22 or newer in the consuming application. When using `@lambdacurry/forms` with `remix-hook-form` in a React Router v7 or v8 application, you need to configure Vite to bundle these packages together to share the router context. Without this, you may encounter the error: diff --git a/docs/consumer-setup-guide.md b/docs/consumer-setup-guide.md index 5d3c5638..381963cc 100644 --- a/docs/consumer-setup-guide.md +++ b/docs/consumer-setup-guide.md @@ -2,7 +2,7 @@ This guide covers how to integrate `@lambdacurry/forms` with React Router v7 or v8 applications using remix-hook-form. -`react-router` (`^7 || ^8`) and `remix-hook-form` are peer dependencies of `@lambdacurry/forms`; install them in your application. The package no longer references `react-router-dom`, which was removed in React Router v8. React Router v8 requires Node 22.22 or newer. +`@lambdacurry/forms` declares four peer dependencies that your application must install: `react` (`^19`), `react-router` (`^7 || ^8`), `remix-hook-form` (`7.1.0`) and `zod` (`^3.24 || ^4`). The package no longer references `react-router-dom`, which was removed in React Router v8. React Router v8 requires Node 22.22 or newer. ## React Router Vite Configuration From 58ce83669326e58a16864d2c6604315bb321aae4 Mon Sep 17 00:00:00 2001 From: Mohsen Ghaemmaghami Date: Thu, 17 Sep 2026 13:25:51 +0300 Subject: [PATCH 6/6] docs: quote the exact peer-dependency ranges Co-Authored-By: Claude Fable 5.1 --- docs/consumer-setup-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/consumer-setup-guide.md b/docs/consumer-setup-guide.md index 381963cc..270a8c46 100644 --- a/docs/consumer-setup-guide.md +++ b/docs/consumer-setup-guide.md @@ -2,7 +2,7 @@ This guide covers how to integrate `@lambdacurry/forms` with React Router v7 or v8 applications using remix-hook-form. -`@lambdacurry/forms` declares four peer dependencies that your application must install: `react` (`^19`), `react-router` (`^7 || ^8`), `remix-hook-form` (`7.1.0`) and `zod` (`^3.24 || ^4`). The package no longer references `react-router-dom`, which was removed in React Router v8. React Router v8 requires Node 22.22 or newer. +`@lambdacurry/forms` declares four peer dependencies that your application must install: `react` (`^19.0.0`), `react-router` (`^7.0.0 || ^8.0.0`), `remix-hook-form` (`7.1.0`) and `zod` (`^3.24.1 || ^4.0.0`). The package no longer references `react-router-dom`, which was removed in React Router v8. React Router v8 requires Node 22.22 or newer. ## React Router Vite Configuration