-
-
Notifications
You must be signed in to change notification settings - Fork 118
Description
Signals no longer auto-unwrap in JSX attributes for boolean props
- Check if updating to the latest version resolves the issue
Environment
- I am using
@preact/signals-core - I am using
@preact/signals - I am using
@preact/signals-react- I understand usage changed in v2, and I've followed the React Integration instructions
Versions:
@preact/signals-react:3.6.0@preact/signals-react-transform:0.6.0react:19.2.0react-dom:19.2.0@types/react:19.2.2@types/react-dom:19.2.2
Describe the bug
Signals are no longer auto-unwrapping when passed directly to JSX attributes, particularly for boolean props. Previously, signals could be passed directly (e.g., checked={signal}, disabled={signal}), but now they require explicit .value access (e.g., checked={signal.value}).
This appears to be inconsistent with the official demo code which shows signals being used without .value in JSX attributes.
To Reproduce
The official nesting demo shows this pattern (lines 62-71):
<tr>
<td>boolKey:</td>
<td>{obj.boolKey}</td>
<td>
<input
type="checkbox"
onChange={e => (obj.boolKey.value = e.currentTarget.checked)}
checked={obj.boolKey} // ← Signal without .value
/>
</td>
</tr>This pattern no longer works in version 3.6.0. We must now use:
<tr>
<td>boolKey:</td>
<td>{obj.boolKey.value.toString()}</td> // ← Need .value
<td>
<input
type="checkbox"
onChange={e => (obj.boolKey.value = e.currentTarget.checked)}
checked={obj.boolKey.value} // ← Need .value
/>
</td>
</tr>Minimal reproduction:
import React from "react";
import { signal } from "@preact/signals-react";
import { useSignals } from "@preact/signals-react/runtime";
const obj = {
boolKey: signal(false)
};
export default function TestComponent() {
useSignals();
return (
<>
{/* ❌ Doesn't work - checkbox doesn't reflect signal value */}
<input
type="checkbox"
checked={obj.boolKey}
onChange={e => (obj.boolKey.value = e.currentTarget.checked)}
/>
{/* ✅ Works - but requires .value */}
<input
type="checkbox"
checked={obj.boolKey.value}
onChange={e => (obj.boolKey.value = e.currentTarget.checked)}
/>
{/* Same issue with disabled attribute */}
<button disabled={obj.boolKey}>Test (broken)</button>
<button disabled={obj.boolKey.value}>Test (works)</button>
</>
);
}Steps to reproduce:
- Clone or reference the official nesting demo
- Try using
checked={signal}on a checkbox input - Observe that the checkbox doesn't reflect the signal value
- Change to
checked={signal.value}and it works
Expected behavior
Based on the official demo code, signals should auto-unwrap when passed to JSX attributes.
From the demo (line 69):
checked={obj.boolKey} // Should work without .valueThis pattern is shown in the official examples and should work. Signals should unwrap automatically when:
- Used in JSX children:
<div>{signal}</div>✅ Works - Used in JSX attributes:
<input checked={signal} />❌ Broken
Actual behavior
- Without
.value: The signal object itself is passed to the DOM attribute. The attribute doesn't reflect the signal value or update reactively. - With
.value: It works correctly, but contradicts the official demo code.
Interestingly, signals work fine in text content:
<td>{obj.boolKey}</td> // ✅ Works - shows true/falseBut fail in attributes:
<input checked={obj.boolKey} /> // ❌ Broken - needs .valueAdditional context
- We're calling
useSignals()at the component level as per the integration guide - Using React 19.2.0 - This might be relevant as React 19 introduced changes to how props are handled
- This pattern used to work in previous versions/configurations
- String signals in text content work fine:
<td>{signal}</td>✅ - Boolean signals in text content work fine:
<td>{boolSignal}</td>✅ - But boolean signals in attributes don't work:
<input checked={boolSignal} />❌
What works:
<td>{obj.boolKey}</td> // ✅ Auto-unwraps
<input checked={obj.boolKey.value} /> // ✅ Explicit .valueWhat doesn't work (but is shown in official demos):
<input checked={obj.boolKey} /> // ❌ Should auto-unwrap
<button disabled={disabledSignal} /> // ❌ Should auto-unwrapQuestion
The official nesting demo shows checked={obj.boolKey} without .value. Is this:
- A bug in the demo code that should be updated to use
.value? - A regression where this used to work but no longer does?
- A React 19 compatibility issue that needs to be addressed?
If .value is always required for attributes, the demo code and documentation should be updated to reflect this. Otherwise, this appears to be a bug where signal auto-unwrapping in attributes is broken.