Skip to content

WIP: feat(solid-form): support Solid 2 beta 6#2122

Draft
brenelz wants to merge 2 commits intoTanStack:mainfrom
brenelz:solid-v2-beta
Draft

WIP: feat(solid-form): support Solid 2 beta 6#2122
brenelz wants to merge 2 commits intoTanStack:mainfrom
brenelz:solid-v2-beta

Conversation

@brenelz
Copy link
Copy Markdown

@brenelz brenelz commented Apr 11, 2026

Update the package runtime, tests, and Vite test setup for the Solid 2 beta API surface so solid-form can build and test against beta 6.

Summary by CodeRabbit

  • Dependencies

    • Updated Solid.js to 2.0.0-beta.6 with adjusted peer dependency constraints
    • Upgraded vite-plugin-solid to 3.0.0-next.4
    • Added @solidjs/web 2.0.0-beta.6 and testing library support
  • Improvements

    • Refactored internal form field and lifecycle handling for better compatibility with Solid.js 2.0
    • Enhanced testing infrastructure

Update the package runtime, tests, and Vite test setup for the Solid 2 beta API surface so solid-form can build and test against beta 6.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 11, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 55c44923-b029-4e7f-a126-18c0529d9ec9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR updates the solid-form package to SolidJS 2.0.0-beta.6, refactoring lifecycle management from onMount/createComputed to onSettled/createRenderEffect patterns, replacing prop utilities with merge/omit, adding a custom testing-library implementation, and updating tests to use Repeat instead of Index for array rendering.

Changes

Cohort / File(s) Summary
Package Dependencies
packages/solid-form/package.json
Bumped solid-js to fixed 2.0.0-beta.6, updated peer dependency constraint to >=2.0.0-beta.6 <2.0.0-experimental.0, added @solidjs/web@2.0.0-beta.6 and @testing-library/dom^10.4.0 to devDependencies, upgraded vite-plugin-solid to fixed 3.0.0-next.4.
Lifecycle Refactoring
packages/solid-form/src/createForm.tsx, packages/solid-form/src/createField.tsx, packages/solid-form/src/createFieldGroup.tsx
Replaced onMount/onCleanup with onSettled, replaced createComputed with createRenderEffect for reactive effect handling. In createFieldGroup, refactored AppField and Field components to use function-based field option closures.
Prop Composition Refactoring
packages/solid-form/src/createFormHook.tsx
Replaced Solid mergeProps/splitProps with merge/omit utilities, simplified conditional merge logic in withForm and withFieldGroup, refactored AppField to inline children invocation.
Testing Infrastructure
packages/solid-form/tests/testing-library.ts, packages/solid-form/vite.config.ts
Added new custom testing-library implementation with render and cleanup functions for SolidJS component testing. Updated Vite config to alias @solidjs/testing-library to the new testing-library module.
Test Updates
packages/solid-form/tests/createField.test.tsx, packages/solid-form/tests/createForm.test.tsx, packages/solid-form/tests/createFormHook.test.tsx
Replaced Solid Index control with Repeat for array-mode rendering across multiple test cases; updated createEffect patterns to use explicit dependency/accessor function syntax; adjusted iterator callback signatures accordingly.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 Lifecycle spins in circles new,
From onMount to onSettled true,
Props merge cleaner, tests repeat,
SolidJS beta makes the beat!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description clearly summarizes the changes but lacks the required template structure including 'Changes' section, Checklist, and 'Release Impact' details needed for proper PR documentation. Add the full PR template with explicit 'Changes' details explaining the API migration, confirm checklist items completion, and clarify whether a changeset is needed for this feature.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main change—updating solid-form to support Solid 2 beta 6—which aligns with the changeset's core objective across dependency updates, lifecycle API changes, and test infrastructure modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Apr 11, 2026

View your CI Pipeline Execution ↗ for commit 9216ee2


☁️ Nx Cloud last updated this comment at 2026-04-11 22:39:34 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 11, 2026

More templates

@tanstack/angular-form

npm i https://pkg.pr.new/@tanstack/angular-form@2122

@tanstack/form-core

npm i https://pkg.pr.new/@tanstack/form-core@2122

@tanstack/form-devtools

npm i https://pkg.pr.new/@tanstack/form-devtools@2122

@tanstack/lit-form

npm i https://pkg.pr.new/@tanstack/lit-form@2122

@tanstack/react-form

npm i https://pkg.pr.new/@tanstack/react-form@2122

@tanstack/react-form-devtools

npm i https://pkg.pr.new/@tanstack/react-form-devtools@2122

@tanstack/react-form-nextjs

npm i https://pkg.pr.new/@tanstack/react-form-nextjs@2122

@tanstack/react-form-remix

npm i https://pkg.pr.new/@tanstack/react-form-remix@2122

@tanstack/react-form-start

npm i https://pkg.pr.new/@tanstack/react-form-start@2122

@tanstack/solid-form

npm i https://pkg.pr.new/@tanstack/solid-form@2122

@tanstack/solid-form-devtools

npm i https://pkg.pr.new/@tanstack/solid-form-devtools@2122

@tanstack/svelte-form

npm i https://pkg.pr.new/@tanstack/svelte-form@2122

@tanstack/vue-form

npm i https://pkg.pr.new/@tanstack/vue-form@2122

commit: 977f613

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/solid-form/tests/createField.test.tsx`:
- Line 4: The test imports Repeat from 'solid-js' which doesn't exist in Solid
2.0; remove the Repeat import and replace any usage of Repeat with For
keyed={false} from 'solid-js' (keep Show import as-is), adapting the render
callback to the For signature (item, i) => ... where item and i are accessors;
if you actually need an index-only Repeat component, import it from
`@solid-primitives/range` instead.

In `@packages/solid-form/tests/testing-library.ts`:
- Around line 23-25: The container selection logic currently can remove
caller-provided containers and fails to clean up auto-created containers under a
custom baseElement, and returning raw dispose prevents deregistering on manual
unmount; fix by introducing an ownedContainer boolean (set true when you create
the div for container) next to baseElement/container logic, wrap the returned
dispose in a custom unmount function that (1) calls the original dispose, (2) if
ownedContainer is true removes the container from the DOM, and (3) removes the
entry from your internal registry so cleanup() only iterates and removes
containers that were created (owned) by this module; update cleanup() to check
ownedContainer/registry flags before removing nodes and ensure all places return
the custom unmount instead of the raw dispose.

In `@packages/solid-form/vite.config.ts`:
- Line 3: ESLint flagged the import order: move the "fileURLToPath" import from
node:url so it appears before the vitest/config import; update the top of
vite.config.ts to import { fileURLToPath } from 'node:url' prior to any import
from 'vitest/config' (so adjust the sequence around the existing import lines in
this file).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6909c33b-d852-4dc9-a4b4-d20422c73d6a

📥 Commits

Reviewing files that changed from the base of the PR and between 254f157 and 977f613.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • packages/solid-form/package.json
  • packages/solid-form/src/createField.tsx
  • packages/solid-form/src/createFieldGroup.tsx
  • packages/solid-form/src/createForm.tsx
  • packages/solid-form/src/createFormHook.tsx
  • packages/solid-form/tests/createField.test.tsx
  • packages/solid-form/tests/createForm.test.tsx
  • packages/solid-form/tests/createFormHook.test.tsx
  • packages/solid-form/tests/testing-library.ts
  • packages/solid-form/vite.config.ts

import { render, waitFor } from '@solidjs/testing-library'
import { userEvent } from '@testing-library/user-event'
import { Index, Show } from 'solid-js'
import { Repeat, Show } from 'solid-js'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

SolidJS 2.0 Repeat component API signature

💡 Result:

SolidJS 2.0 does not have a built-in "Repeat" component in core. Core control flow components are , , /, (replaces Suspense), and (replaces ErrorBoundary). In SolidJS 2.0 beta: - from 1.x is replaced by - children now receive accessors: (item, i) => ... where item and i are called. A component exists in the third-party @solid-primitives/range package (updated Feb 2026), with API: function Repeat<T>(props: { times: number; fallback?: T; children: ((index: number) => T) | T; }): Accessor<T[]>; Example: <Repeat times={10}> <div></div> </Repeat> or with render prop: <Repeat times={10}> {index => <div>{index}</div>} </Repeat> No evidence of core Repeat in 2.0 docs/RFCs/release notes. Official 2.0 docs at docs.solidjs.com cover 1.x; 2.0 migration in GitHub next branch.

Citations:


Repeat is not exported from solid-js 2.0 — use <For keyed={false}> instead.

Repeat does not exist in SolidJS 2.0 core. The import at line 4 will fail. The correct migration from Index is <For keyed={false}>, which receives the signature (item, i) => ... where both are accessor functions. If a Repeat component is needed for index-only iteration, it exists as a third-party component in @solid-primitives/range (not in core solid-js).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-form/tests/createField.test.tsx` at line 4, The test imports
Repeat from 'solid-js' which doesn't exist in Solid 2.0; remove the Repeat
import and replace any usage of Repeat with For keyed={false} from 'solid-js'
(keep Show import as-is), adapting the render callback to the For signature
(item, i) => ... where item and i are accessors; if you actually need an
index-only Repeat component, import it from `@solid-primitives/range` instead.

Comment on lines +23 to +25
const baseElement = options.baseElement ?? document.body
const container =
options.container ?? baseElement.appendChild(document.createElement('div'))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/solid-form/tests/testing-library.ts

Repository: TanStack/form

Length of output: 2198


Track container ownership and deregister on unmount to prevent cleanup bugs.

Current implementation has three reliability problems:

  • Line 60 can remove caller-provided containers. The parentNode check doesn't distinguish between auto-created and caller-managed containers.
  • Auto-created containers under custom baseElement are not cleaned up (parentNode won't equal document.body).
  • Line 51 returns raw dispose, so manual unmount() calls don't remove the registry entry, risking stale references and double disposal when cleanup() runs.

Add ownedContainer boolean to track ownership, wrap cleanup logic in a custom unmount function, and update cleanup() to only remove containers this module created:

🔧 Proposed fix
 type RenderRef = {
   container: HTMLElement
   dispose: () => void
+  ownedContainer: boolean
 }
@@
 export function render(
@@
 ) {
   const baseElement = options.baseElement ?? document.body
-  const container =
-    options.container ?? baseElement.appendChild(document.createElement('div'))
+  const ownedContainer = !options.container
+  const container =
+    options.container ?? baseElement.appendChild(document.createElement('div'))
@@
-  mountedContainers.add({ container, dispose })
+  const ref: RenderRef = { container, dispose, ownedContainer }
+  mountedContainers.add(ref)
@@
+  const unmount = () => {
+    ref.dispose()
+    if (ref.ownedContainer && ref.container.parentNode) {
+      ref.container.parentNode.removeChild(ref.container)
+    }
+    mountedContainers.delete(ref)
+  }
+
   return {
@@
-    unmount: dispose,
+    unmount,
     ...queryHelpers,
   }
 }
@@
 export function cleanup() {
   for (const ref of Array.from(mountedContainers)) {
     ref.dispose()
 
-    if (ref.container.parentNode === document.body) {
-      document.body.removeChild(ref.container)
+    if (ref.ownedContainer && ref.container.parentNode) {
+      ref.container.parentNode.removeChild(ref.container)
     }
 
     mountedContainers.delete(ref)
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const baseElement = options.baseElement ?? document.body
const container =
options.container ?? baseElement.appendChild(document.createElement('div'))
type RenderRef = {
container: HTMLElement
dispose: () => void
ownedContainer: boolean
}
export function render(
Component: ParentComponent,
options: RenderOptions = {},
) {
const baseElement = options.baseElement ?? document.body
const ownedContainer = !options.container
const container =
options.container ?? baseElement.appendChild(document.createElement('div'))
// ... component mounting code ...
const ref: RenderRef = { container, dispose, ownedContainer }
mountedContainers.add(ref)
const unmount = () => {
ref.dispose()
if (ref.ownedContainer && ref.container.parentNode) {
ref.container.parentNode.removeChild(ref.container)
}
mountedContainers.delete(ref)
}
return {
container,
unmount,
...queryHelpers,
}
}
export function cleanup() {
for (const ref of Array.from(mountedContainers)) {
ref.dispose()
if (ref.ownedContainer && ref.container.parentNode) {
ref.container.parentNode.removeChild(ref.container)
}
mountedContainers.delete(ref)
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-form/tests/testing-library.ts` around lines 23 - 25, The
container selection logic currently can remove caller-provided containers and
fails to clean up auto-created containers under a custom baseElement, and
returning raw dispose prevents deregistering on manual unmount; fix by
introducing an ownedContainer boolean (set true when you create the div for
container) next to baseElement/container logic, wrap the returned dispose in a
custom unmount function that (1) calls the original dispose, (2) if
ownedContainer is true removes the container from the DOM, and (3) removes the
entry from your internal registry so cleanup() only iterates and removes
containers that were created (owned) by this module; update cleanup() to check
ownedContainer/registry flags before removing nodes and ensure all places return
the custom unmount instead of the raw dispose.

@@ -1,10 +1,20 @@
import { defineConfig, mergeConfig } from 'vitest/config'
import { tanstackViteConfig } from '@tanstack/vite-config'
import { fileURLToPath } from 'node:url'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Import order issue flagged by ESLint.

The node:url import should be placed before vitest/config to follow the project's import ordering conventions.

🔧 Suggested fix
+import { fileURLToPath } from 'node:url'
 import { defineConfig, mergeConfig } from 'vitest/config'
 import { tanstackViteConfig } from '@tanstack/vite-config'
-import { fileURLToPath } from 'node:url'
 import solid from 'vite-plugin-solid'
🧰 Tools
🪛 ESLint

[error] 3-3: node:url import should occur before import of vitest/config

(import/order)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-form/vite.config.ts` at line 3, ESLint flagged the import
order: move the "fileURLToPath" import from node:url so it appears before the
vitest/config import; update the top of vite.config.ts to import { fileURLToPath
} from 'node:url' prior to any import from 'vitest/config' (so adjust the
sequence around the existing import lines in this file).

@brenelz brenelz marked this pull request as draft April 11, 2026 22:34
Update solid-form packaging, supporting examples, and Solid package manifests so the Solid 2 beta toolchain resolves consistently across the workspace.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant