TanStack / form

๐Ÿค– Powerful and type-safe form state management for the web. TS/JS, React Form, Solid Form, Lit Form and Vue Form.

Home Page:https://tanstack.com/form

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

"A component is changing a controlled input to be uncontrolled" in Next.js App router

jakst opened this issue ยท comments

Describe the bug

When setting up the most barebones form with tanstack form in Next.js with the app router, I get the below error. It works fine with the pages router however.

Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
    at input
    at Field (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/@tanstack+react-form@0.9.0_react-dom@18.2.0_react@18.2.0/node_modules/@tanstack/react-form/build/modern/useField.js:44:3)
    at form
    at Provider (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/@tanstack+react-form@0.9.0_react-dom@18.2.0_react@18.2.0/node_modules/@tanstack/react-form/build/modern/useForm.js:23:95)
    at UserForm (webpack-internal:///(app-pages-browser)/./app/form.tsx:12:79)
    at main
    at InnerLayoutRouter (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:240:11)
    at RedirectErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/redirect-boundary.js:72:9)
    at RedirectBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/redirect-boundary.js:80:11)
    at NotFoundErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/not-found-boundary.js:54:9)
    at NotFoundBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/not-found-boundary.js:62:11)
    at LoadingBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:345:11)
    at ErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/error-boundary.js:130:11)
    at InnerScrollAndFocusHandler (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:151:9)
    at ScrollAndFocusHandler (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:226:11)
    at RenderFromTemplateContext (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/render-from-template-context.js:15:44)
    at OuterLayoutRouter (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:355:11)
    at body
    at html
    at RedirectErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/redirect-boundary.js:72:9)
    at RedirectBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/redirect-boundary.js:80:11)
    at NotFoundErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/not-found-boundary.js:54:9)
    at NotFoundBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/not-found-boundary.js:62:11)
    at DevRootNotFoundBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/dev-root-not-found-boundary.js:32:11)
    at ReactDevOverlay (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/react-dev-overlay/internal/ReactDevOverlay.js:66:9)
    at HotReload (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js:295:11)
    at Router (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/app-router.js:169:11)
    at ErrorBoundaryHandler (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/error-boundary.js:100:9)
    at ErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/error-boundary.js:130:11)
    at AppRouter (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/app-router.js:451:13)
    at ServerRoot (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/app-index.js:128:11)
    at RSCComponent
    at Root (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.0.3_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/app-index.js:144:11)

This is with this very simple form

"use client"
import { useForm } from "@tanstack/react-form"

export function UserForm() {
  const form = useForm({
    defaultValues: {
      name: "John Doe",
    },
  })

  return (
    <form.Provider>
      <form>
        <form.Field name="name">
          {(field) => (
            <input
              name={field.name}
              value={field.state.value}
              onChange={(event) => {
                field.handleChange(event.target.value)
              }}
            />
          )}
        </form.Field>
      </form>
    </form.Provider>
  )
}

Your minimal, reproducible example

https://github.com/jakst/tanstack-form-error-message

Steps to reproduce

  1. Clone repo, install deps and run pnpm dev
  2. Visit http://localhost:3000 (or whichever port it started on)
  3. Open the console and notice the error message described above has been printed
  4. Click the link at the bottom that takes you to the same form but in the pages router, notice it does not print an error message to the console

Expected behavior

I expect the form to not generate any errors

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

  • MacOS 14.1.1
  • Chrome v119

Tanstack Form adapter

react-form

TanStack Form version

v0.9.0

TypeScript version

No response

Additional context

No response

A temporary workaround is to only render the children if !== undefined

{(field) => (field.state.value !== undefined && (<><input ... /></>))}
...
value={field.state.value ?? ""}
...

Yeah that works for now. Good idea, thanks! But looking forward to not having to do that ๐Ÿ™‚

...
value={field.state.value ?? ""}
...

This approach will cause issues when validating.

I think I've solved this issue ๐Ÿ˜„ I'm waiting to release TanStack Form 0.12 until I can figure out how our SSR/SSG API will work with Remix, but expect this fix to be rolled out alongside some fun Next.js-specific APIs (Namely, Server Action support)