Skip to content

fix(ui): stop capturing Platform in on-device UI worklets - #967

Open
officialasishkumar wants to merge 1 commit into
storybookjs:nextfrom
officialasishkumar:fix/worklet-platform-capture
Open

officialasishkumar wants to merge 1 commit into
storybookjs:nextfrom
officialasishkumar:fix/worklet-platform-capture

Conversation

@officialasishkumar

Copy link
Copy Markdown
Contributor

Issue: #935

What I did

On React Native 0.87 with react-native-worklets 0.12, the on-device UI crashes as soon as MobileMenuDrawer mounts. The error is [Worklets] Trying to convert a cyclic object to a serializable, or an error thrown from a native module getter while Worklets walks the captured values.

Two useAnimatedStyle worklets read Platform.OS inside the worklet: the menu backdrop's androidTouchEventFix and the addons panel's extraPadding. The worklets Babel plugin captures every outer value a worklet references, and Worklets copies those values to the UI runtime. From source, that means these worklets carried Platform with them, and its getters call into native modules. In the published CommonJS build the import compiles to react_native.Platform.OS, so they carried the entire react-native module.

Running the worklets Babel plugin over the built dist/index.js shows this.

Before:

indexJs1.__closure = [react_native, insets, height, animatedPosition];
indexJs2.__closure = [react_native, backdropComponentProps];

After:

indexJs1.__closure = [isAndroid$1, insets, height, animatedPosition];
indexJs2.__closure = [isAndroid, animatedIndex];

Changes:

  • MobileMenuDrawer.tsx and MobileAddonsPanel.tsx read Platform.OS === 'android' once at module level into isAndroid, and the worklets use that, so they only capture a boolean. The backdrop worklet also destructures animatedIndex instead of capturing the whole props object. The non-worklet paddingTop check in the backdrop uses the same constant.
  • New src/worklets.test.tsx renders both components and checks that the __closure of every useAnimatedStyle worklet is set but doesn't contain Platform. Both tests fail on next.
  • jest.config.js: added storybook/ to transformIgnorePatterns so MobileAddonsPanel can be rendered under jest. This is the same one-line change as fix(ui): stop passing useAnimatedStyle dependencies on native #948.
  • Changeset: patch for @storybook/react-native-ui.

These are the only worklets in the packages. @storybook/react-native-ui-lite doesn't use Reanimated.

Note for merging with #948: both PRs touch the adjustedBottomSheetSize worklet, so whichever lands second gets a small conflict there. Keep #948's useAnimatedStyle(() => ..., Platform.OS === 'web' ? [...] : undefined) form and use isAndroid inside the worklet.

How to test

pnpm -F @storybook/react-native-ui test

The new worklets.test.tsx passes. If you revert MobileMenuDrawer.tsx and MobileAddonsPanel.tsx, both of its tests fail, and the failure output shows the Platform object inside the received closure.

Manually, follow the issue's reproduction: React Native 0.87, react-native-reanimated 4.6 and react-native-worklets 0.12, with the on-device UI mounted at launch. The UI should open instead of crashing. On Android, the menu backdrop should still sit behind the sheet when the menu is closed, and the addons panel's maximum height should be unchanged on both platforms.

  • Does this need a new example in examples/expo-example? No
  • Does this need an update to the documentation? No

Fixes #935

On React Native 0.87 with react-native-worklets 0.12 the on-device UI
crashes as soon as MobileMenuDrawer mounts, with "[Worklets] Trying to
convert a cyclic object to a serializable" or errors thrown from
native module getters while Worklets walks the captured values.

The menu backdrop and the addons panel both read `Platform.OS` inside a
`useAnimatedStyle` worklet. The worklets babel plugin captures every
outer value a worklet references and Worklets copies them to the UI
runtime, so these worklets carried `Platform` with them, and in the
published CommonJS build, where the import compiles to
`react_native.Platform.OS`, the entire `react-native` module.

Read `Platform.OS` once at module level into `isAndroid` and use that in
the worklets, so they only capture a boolean. The backdrop worklet also
destructures `animatedIndex` instead of capturing the whole props object.

Add a jest test that checks the worklets' `__closure` no longer contains
`Platform`; it fails without the fix. Rendering MobileAddonsPanel under
jest needs `storybook/` in transformIgnorePatterns, the same change storybookjs#948
makes.

Fixes storybookjs#935
@changeset-bot

changeset-bot Bot commented Nov 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 37054c4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@storybook/react-native-ui Patch
@storybook/addon-ondevice-actions Patch
@storybook/addon-ondevice-backgrounds Patch
@storybook/addon-ondevice-controls Patch
@storybook/addon-ondevice-notes Patch
@storybook/react-native-theming Patch
@storybook/react-native-ui-common Patch
@storybook/react-native-ui-lite Patch
@storybook/react-native Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

Copy link
Copy Markdown
Codex Review Summary: ❌ Failed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ❌ Failed 2026-11-10T12:49:30.252851Z
We couldn’t complete this review. Comment @codex review to try again.
37054c4 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

This branch has not been deployed

No deployments
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.

On-device UI crashes on React Native 0.87 with "[Worklets] Trying to convert a cyclic object to a serializable"

1 participant