Description
Verify canary release
- I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System:
Platform: linux
Arch: arm64
Version: #78-Ubuntu SMP Tue Apr 18 09:00:08 UTC 2023
Binaries:
Node: 19.8.1
npm: 9.5.1
Yarn: 1.22.19
pnpm: N/A
Relevant packages:
next: 13.3.1
eslint-config-next: N/A
react: 18.2.0
react-dom: 18.2.0
Which area(s) of Next.js are affected? (leave empty if unsure)
App directory (appDir: true)
Link to the code that reproduces this issue
https://github.com/christianjuth/next-pages-radio-bug
To Reproduce
Note: this bug appears in 13.0.1+ including 13.4.2-canary.3
This bug appears in both a pages/ and app/. It only appears in the pages/ route when app dir is enabled. In other words, enabling app dir seems to have an effect on the pages dir.
Recreate the bug with pages/
- Create a new Next.js app using
npx create-next-app@latest
(opt out of TS, eslint, and Tailwind for simplicity) - Make sure the app directory is enabled (next.config.js must use experimental.appDir = true prior to Next.js 13.4.0).
- Modify either pages/pages.jsx with the following:
import { useState } from 'react'; export default function Test() { const [selectedTopping, setSelectedTopping] = useState('Medium'); return ( <div className="flex flex-col items-center"> <input type="radio" name="topping" value="Regular" id="regular" checked={selectedTopping === 'Regular'} onChange={e => setSelectedTopping(e.target.value)} /> <label htmlFor="regular">Regular</label> <input type="radio" name="topping" value="Medium" id="medium" checked={selectedTopping === 'Medium'} onChange={e => setSelectedTopping(e.target.value)} /> <label htmlFor="medium">Medium</label> <input type="radio" name="topping" value="Large" id="large" checked={selectedTopping === 'Large'} onChange={e => setSelectedTopping(e.target.value)} /> <label htmlFor="large">Large</label> </div> ) }
- Run your app in dev mode using
yarn dev
. If you visit http://localhost:3000/pages you will notice the controlled radio button is initially selected but then flickers uncollected
Recreate the bug with app/
- Create a new Next.js app using
npx create-next-app@latest
(opt out of TS, eslint, and Tailwind for simplicity) - Make sure the app directory is enabled (next.config.js must use experimental.appDir = true prior to Next.js 13.4.0).
- Modify either app/page.js with the following:
'use client' import { useState } from 'react'; function Test() { const [selectedTopping, setSelectedTopping] = useState('Medium'); return ( <div className="flex flex-col items-center"> <input type="radio" name="topping" value="Regular" id="regular" checked={selectedTopping === 'Regular'} onChange={e => setSelectedTopping(e.target.value)} /> <label htmlFor="regular">Regular</label> <input type="radio" name="topping" value="Medium" id="medium" checked={selectedTopping === 'Medium'} onChange={e => setSelectedTopping(e.target.value)} /> <label htmlFor="medium">Medium</label> <input type="radio" name="topping" value="Large" id="large" checked={selectedTopping === 'Large'} onChange={e => setSelectedTopping(e.target.value)} /> <label htmlFor="large">Large</label> </div> ) } export default function Page() { return <Test />; }
- Run your app in dev mode using
yarn dev
. If you visit http://localhost:3000 you will notice the controlled radio button is initially selected but then flickers uncollected
Debugging further
Inspecting the dom shows the following
<div class="flex flex-col items-center">
<input type="radio" name="topping" id="regular" value="Regular">
<label for="regular">Regular</label>
<input type="radio" name="topping" id="medium" value="Medium" checked="">
<label for="medium">Medium</label>
<input type="radio" name="topping" id="large" value="Large">
<label for="large">Large</label>
</div>
However, the input marked checked=""
is not checked and if we query the dom we can see document.getElementById("medium").checked = false
Describe the Bug
There seems to be an issue where React is initially rendering the radio input as selected and then it almost instantly looses it's selected state. My guess is this has something to do with React's strict mode. However, this issue doesn't arise until I enable the appDir.
Expected Behavior
I would expect the controlled radio input selection to visually match the state in React.
Which browser are you using? (if relevant)
1.51.110 Chromium: 113.0.5672.77 (Official Build) (64-bit)
How are you deploying your application? (if relevant)
No response