Before submitting a new issue
Bug summary
Summary
We are seeing an intermittent native iOS crash when rendering a list of selectable items using HeroUI Native ControlField + Checkbox, especially when several rows update selection/disabled state quickly.
The crash does not surface as a JavaScript error in Metro. The app process aborts natively and iOS crash reports consistently point to React Native Fabric view unmounting:
-[RCTViewComponentView unmountChildComponentView:index:]
RCTPerformMountInstructions(...)
-[RCTMountingManager performTransaction:]
After replacing the HeroUI Native ControlField/Checkbox rows with plain React Native Pressable rows and a very simple View-based custom checkbox indicator, the crash appears to stop.
Environment
heroui-native: 1.0.3
expo: 55.0.26
expo-dev-client: 55.0.35
react: 19.2.0
react-native: 0.83.6
react-native-reanimated: 4.2.1
react-native-svg: 15.15.3
react-native-worklets: 0.7.4
react-native-screens: 4.23.0
react-native-gesture-handler: 2.30.0
node: 24.16.0
pnpm: 11.1.2
Device/runtime:
Platform: iOS physical device
iOS version from crash report: iPhone OS 26.4.2
Build type: Expo dev client / TestFlight-installed dev build
What we were rendering
A scrollable list of rows, each row roughly like this:
<ControlField
isSelected={isSelected}
isDisabled={isDisabled}
onSelectedChange={(nextSelected) => {
toggleItem(item.id, nextSelected);
}}
>
<ControlField.Indicator>
<Checkbox />
</ControlField.Indicator>
<View>
<Text>{item.label}</Text>
<Text>{item.description}</Text>
</View>
</ControlField>
The list had around 6 items.
The relevant behavior:
- User can select up to
N items.
- Once
N items are selected, the remaining unselected rows become disabled.
- If the user unselects one item, disabled rows become enabled again.
- There is also a separate acknowledgement checkbox below the list.
- Rapidly toggling rows, selecting/unselecting, and changing disabled state could crash the app.
Steps to reproduce
A minimal reproduction would likely be:
- Render a scrollable list of multiple
ControlField rows.
- Each row contains
ControlField.Indicator with Checkbox.
- Keep selected IDs in React state.
- Dynamically set each row as disabled when a selection limit is reached.
- Rapidly tap rows:
- select item A
- select item B
- unselect item A
- select item C
- repeat quickly
- On iOS physical device, the app sometimes aborts natively.
Example logic:
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const limit = 2;
return (
<ScrollView>
{items.map((item) => {
const isSelected = selectedIds.includes(item.id);
const isDisabled = !isSelected && selectedIds.length >= limit;
return (
<ControlField
key={item.id}
isSelected={isSelected}
isDisabled={isDisabled}
onSelectedChange={(nextSelected) => {
setSelectedIds((current) => {
if (nextSelected) return [...current, item.id];
return current.filter((id) => id !== item.id);
});
}}
>
<ControlField.Indicator>
<Checkbox />
</ControlField.Indicator>
<View>
<Text>{item.label}</Text>
</View>
</ControlField>
);
})}
</ScrollView>
);
Actual result
The app crashes natively. No useful JS error is shown in Metro.
Crash report excerpt:
Exception Type: EXC_CRASH (SIGABRT)
Termination Reason: SIGNAL 6 Abort trap: 6
Last Exception Backtrace:
__exceptionPreprocess
objc_exception_throw
-[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:]
-[RCTViewComponentView unmountChildComponentView:index:]
RCTPerformMountInstructions(...)
-[RCTMountingManager performTransaction:]
-[RCTMountingManager initiateTransaction:]
__42-[RCTMountingManager scheduleTransaction:]_block_invoke
__RCTExecuteOnMainQueue_block_invoke
_dispatch_call_block_and_release
_dispatch_main_queue_drain
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
Expected result
The list should update selected/disabled visual state without crashing the native app process.
What we tried
The crash stopped after changing the implementation to:
- No
ControlField.
- No HeroUI Native
Checkbox.
- Plain React Native
Pressable rows.
- No dynamic native
disabled prop on the rows.
- Disabled behavior handled with a JS guard:
onPress={() => {
if (isDisabled) return;
toggleItem(item.id);
}}
- Accessibility state still exposes disabled/checked:
accessibilityRole="checkbox"
accessibilityState={{
checked: isSelected,
disabled: isDisabled,
}}
- Custom checkbox indicator made only with
View styles, no Svg.
collapsable={false} added to some critical row/container views.
We later reintroduced our scroll shadow wrapper and the crash still did not reproduce, so the main suspect seems to be the combination of:
ControlField
Checkbox
- rapid
isSelected changes
- dynamic disabled state in multiple rows
- React Native Fabric mounting/unmounting
Additional notes
The crash was intermittent but reproducible by aggressively toggling the items. It happened multiple times with the same native stack.
It would be useful to know if ControlField/Checkbox internally mounts/unmounts native children or animated/SVG children when isSelected or isDisabled changes. The native stack suggests React Native Fabric is trying to unmount a child component view at an index that no longer matches the expected native hierarchy.
Please let us know if this is a known issue, if there is a recommended pattern for selectable lists with dynamic disabled rows, or if you want a smaller reproduction project.
Library version
1.0.3
Environment info
Manual environment info from package.json/system:
heroui-native: 1.0.3
expo: 55.0.26
expo-dev-client: 55.0.35
react: 19.2.0
react-native: 0.83.6
react-native-reanimated: 4.2.1
react-native-svg: 15.15.3
react-native-worklets: 0.7.4
react-native-screens: 4.23.0
react-native-gesture-handler: 2.30.0
node: 24.16.0
pnpm: 11.1.2
macOS: 26.5 (25F71)
Xcode: 26.5 (17F42)
Device/runtime:
Platform: iOS physical device
iOS version from crash report: iPhone OS 26.4.2
Build type: Expo dev client / TestFlight-installed dev build
Steps to reproduce
-
Render a scrollable list of multiple selectable rows using HeroUI Native ControlField.
-
Each row should contain ControlField.Indicator with a HeroUI Native Checkbox.
-
Keep selected row IDs in React state.
-
Dynamically disable unselected rows when a selection limit is reached.
-
Example behavior:
- User can select up to
N items.
- Once
N items are selected, the remaining unselected rows become disabled.
- If the user unselects one selected item, the previously disabled rows become enabled again.
-
Rapidly toggle rows on a physical iOS device:
- Select item A.
- Select item B.
- Unselect item A.
- Select item C.
- Repeat quickly.
-
The app can intermittently abort natively with no useful JS error in Metro.
Observed native crash stack:
Exception Type: EXC_CRASH (SIGABRT)
Termination Reason: SIGNAL 6 Abort trap: 6
Last Exception Backtrace:
__exceptionPreprocess
objc_exception_throw
-[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:]
-[RCTViewComponentView unmountChildComponentView:index:]
RCTPerformMountInstructions(...)
-[RCTMountingManager performTransaction:]
-[RCTMountingManager initiateTransaction:]
__42-[RCTMountingManager scheduleTransaction:]_block_invoke
__RCTExecuteOnMainQueue_block_invoke
Minimal pseudo-code:
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const limit = 2;
return (
<ScrollView>
{items.map((item) => {
const isSelected = selectedIds.includes(item.id);
const isDisabled = !isSelected && selectedIds.length >= limit;
return (
<ControlField
key={item.id}
isSelected={isSelected}
isDisabled={isDisabled}
onSelectedChange={(nextSelected) => {
setSelectedIds((current) => {
if (nextSelected) return [...current, item.id];
return current.filter((id) => id !== item.id);
});
}}
>
<ControlField.Indicator>
<Checkbox />
</ControlField.Indicator>
<View>
<Text>{item.label}</Text>
</View>
</ControlField>
);
})}
</ScrollView>
);
What stopped the crash in our app:
- Replaced
ControlField with plain React Native Pressable.
- Replaced HeroUI Native
Checkbox with a simple View-based checkbox indicator.
- Avoided changing the native
disabled prop dynamically.
- Kept disabled behavior as a JS guard inside
onPress.
- Kept
accessibilityState.checked and accessibilityState.disabled.
- Added
collapsable={false} to some critical row/container views.
After this change, the crash was no longer reproducible even when rapidly toggling the rows.
Reproducible example repository
No public reproducible repository available yet.
Before submitting a new issue
Bug summary
Summary
We are seeing an intermittent native iOS crash when rendering a list of selectable items using HeroUI Native
ControlField+Checkbox, especially when several rows update selection/disabled state quickly.The crash does not surface as a JavaScript error in Metro. The app process aborts natively and iOS crash reports consistently point to React Native Fabric view unmounting:
After replacing the HeroUI Native
ControlField/Checkboxrows with plain React NativePressablerows and a very simpleView-based custom checkbox indicator, the crash appears to stop.Environment
Device/runtime:
What we were rendering
A scrollable list of rows, each row roughly like this:
The list had around 6 items.
The relevant behavior:
Nitems.Nitems are selected, the remaining unselected rows become disabled.Steps to reproduce
A minimal reproduction would likely be:
ControlFieldrows.ControlField.IndicatorwithCheckbox.Example logic:
Actual result
The app crashes natively. No useful JS error is shown in Metro.
Crash report excerpt:
Expected result
The list should update selected/disabled visual state without crashing the native app process.
What we tried
The crash stopped after changing the implementation to:
ControlField.Checkbox.Pressablerows.disabledprop on the rows.Viewstyles, noSvg.collapsable={false}added to some critical row/container views.We later reintroduced our scroll shadow wrapper and the crash still did not reproduce, so the main suspect seems to be the combination of:
ControlFieldCheckboxisSelectedchangesAdditional notes
The crash was intermittent but reproducible by aggressively toggling the items. It happened multiple times with the same native stack.
It would be useful to know if
ControlField/Checkboxinternally mounts/unmounts native children or animated/SVG children whenisSelectedorisDisabledchanges. The native stack suggests React Native Fabric is trying to unmount a child component view at an index that no longer matches the expected native hierarchy.Please let us know if this is a known issue, if there is a recommended pattern for selectable lists with dynamic disabled rows, or if you want a smaller reproduction project.
Library version
1.0.3
Environment info
Steps to reproduce
Render a scrollable list of multiple selectable rows using HeroUI Native
ControlField.Each row should contain
ControlField.Indicatorwith a HeroUI NativeCheckbox.Keep selected row IDs in React state.
Dynamically disable unselected rows when a selection limit is reached.
Example behavior:
Nitems.Nitems are selected, the remaining unselected rows become disabled.Rapidly toggle rows on a physical iOS device:
The app can intermittently abort natively with no useful JS error in Metro.
Observed native crash stack:
Minimal pseudo-code:
What stopped the crash in our app:
ControlFieldwith plain React NativePressable.Checkboxwith a simpleView-based checkbox indicator.disabledprop dynamically.onPress.accessibilityState.checkedandaccessibilityState.disabled.collapsable={false}to some critical row/container views.After this change, the crash was no longer reproducible even when rapidly toggling the rows.
Reproducible example repository
No public reproducible repository available yet.