Skip to content

Add cancelsJSResponder prop to the gesture handlers#4094

Open
coado wants to merge 33 commits intomainfrom
prevent-recognizers
Open

Add cancelsJSResponder prop to the gesture handlers#4094
coado wants to merge 33 commits intomainfrom
prevent-recognizers

Conversation

@coado
Copy link
Copy Markdown
Contributor

@coado coado commented Apr 16, 2026

Description

Adds a per-gesture cancelsJSResponder?: boolean prop on both platforms to the v3 handler hooks. It defaults to true to preserve the behavior of cancelling RN JS responder system.

On Android it moves the root-view dispatch off onCancel onto new orchestrator callbacks onCancelJSResponderRequested and onCancelJSResponderReleased. The orchestrator only files the "released" callback when the last active handler with cancelsJSResponder: true finishes.

on iOS the RNRootViewGestureRecognizer checks the handler prop before notifying the delegate.

Test plan

Added new test example with multiple handlers that might or might not cancel the JS responder.

Simulator Screenshot - iPhone 16 Pro Max - 2026-04-17 at 12 59 42

Copilot AI review requested due to automatic review settings April 16, 2026 09:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new preventRecognizers configuration option to gesture handlers to control whether GH activation cancels React Native JS responders (with native iOS/Android plumbing + an example to demonstrate behavior).

Changes:

  • Added preventRecognizers to shared gesture config types/whitelists so it can flow from JS to native.
  • Implemented native behavior gates on iOS (root recognizer) and Android (orchestrator → root helper interception) with default true.
  • Added docs + a new common-app example to showcase responder cancellation differences.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/react-native-gesture-handler/src/v3/types/ConfigTypes.ts Adds preventRecognizers to common gesture config typing.
packages/react-native-gesture-handler/src/v3/hooks/utils/propsWhiteList.ts Allows the new config prop to pass through native config filtering.
packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx Minor formatting-only change near detector initialization.
packages/react-native-gesture-handler/src/handlers/gestures/GestureDetector/utils.ts Adds preventRecognizers to allowed prop list for detector plumbing.
packages/react-native-gesture-handler/apple/RNRootViewGestureRecognizer.m iOS: only trigger RN responder cancellation when preventRecognizers is enabled.
packages/react-native-gesture-handler/apple/RNGestureHandler.mm iOS: default + config parsing for preventRecognizers.
packages/react-native-gesture-handler/apple/RNGestureHandler.h iOS: exposes preventRecognizers on the handler.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt Android: intercept/cancel RN touch stream when requested by orchestrator.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt Android: emits request/release callbacks based on handler activation + finish.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt Android: stores/reads preventRecognizers from config with default true.
packages/docs-gesture-handler/docs/fundamentals/root-view.mdx Doc formatting cleanup (indentation/trailing whitespace).
packages/docs-gesture-handler/docs/fundamentals/gesture-detector.mdx Adds docs for preventRecognizers (but currently includes a broken tag and placement concerns).
apps/common-app/src/new_api/tests/rnResponderCancellation/index.tsx New example screen demonstrating RN responder cancellation vs. concurrent handling.
apps/common-app/src/new_api/index.tsx Registers the new example in the app’s examples list.
Comments suppressed due to low confidence (1)

packages/docs-gesture-handler/docs/fundamentals/gesture-detector.mdx:206

  • The closing tag for HeaderWithBadges was changed to </BadHeaderWithBadgesges>, which is not a valid component/tag and will break MDX rendering/build. Replace it with the correct </HeaderWithBadges> closing tag.
<HeaderWithBadges platforms={['web']}>
### touchAction
</HeaderWithBadges>


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/react-native-gesture-handler/apple/RNRootViewGestureRecognizer.m Outdated
Comment thread packages/docs-gesture-handler/docs/fundamentals/gesture-detector.mdx Outdated
@coado
Copy link
Copy Markdown
Contributor Author

coado commented Apr 17, 2026

@copilot review?

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 17, 2026

@copilot review?

Here's the review of the current state of the PR:

Overall: The implementation is solid. All 3 previously flagged issues are resolved. Here's a brief summary per area:

Android:

  • onCancelJSResponderRequested is correctly placed after the FAILED/CANCELLED early-return guard in makeActive — no stale shouldIntercept=true risk for already-finished handlers.
  • onCancelJSResponderReleased checks that no other active handler still has cancelsJSResponder=true before clearing shouldIntercept. ✓

iOS:

  • cancelsJSResponder property defaults to YES in resetConfig, read from config in updateConfig. ✓
  • canBePreventedByGestureRecognizer: gates the delegate call on handler.cancelsJSResponder. ✓

TypeScript:

  • Prop flows correctly: CommonGestureConfig (v3 types) → CommonConfig whitelist (v3) → ALLOWED_PROPS (v2 utils). ✓

Docs: cancelsJSResponder is documented in base-gesture-config.mdx (not under GestureDetector props). ✓

No issues found in the current state.

@coado coado changed the title [DRAFT] Add preventRecognizers prop to the gesture handlers Add preventRecognizers prop to the gesture handlers Apr 23, 2026
@coado coado marked this pull request as ready for review April 23, 2026 14:10
Copy link
Copy Markdown
Member

@j-piasecki j-piasecki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update the title and description with the final prop name?

Also, I don't have an iOS device on hand, so can you check the following scenario?

There are two handlers and js recognizer on the same view. Gesture A with cancelsJSRecognizer: false, and a config that prevents it from activating (like minimumDistance: 100000 on pan). Gesture B has cancelsJSRecognizer: false and requires A to fail before it activates (useExclusiveGestures(A, B)), but should try to activate almost immediately (like pan with minimumDistance: 10). Now, in that scenario, does B trying to activate but getting blocked on A, cancel the JS recognizer, or not?

I've checked Android and there it works as expected (until A fails, the JS recognizer is not interrupted).

Comment thread packages/react-native-gesture-handler/src/v3/detectors/NativeDetector.tsx Outdated
Comment thread packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-config.mdx Outdated
Comment thread packages/react-native-gesture-handler/apple/RNRootViewGestureRecognizer.m Outdated
@coado coado changed the title Add preventRecognizers prop to the gesture handlers Add cancelsJSResponder prop to the gesture handlers Apr 24, 2026
coado and others added 5 commits April 24, 2026 11:40
Copy link
Copy Markdown
Collaborator

@m-bert m-bert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like @j-piasecki caught everything

contentContainerStyle={scrollStyles.content}
showsVerticalScrollIndicator>
<View style={scrollStyles.section}>
<SingleHandlerExample />
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about extracting this (and Multi*) to separate files?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I don't actually like this example so will change that a bit.

Comment on lines +152 to +154
<HeaderWithBadges platforms={['ios', 'android']}>
### cancelsJSResponder
</HeaderWithBadges>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I remember this is now Badges and HeaderWithBadges no longer exists (and if it does, it shouldn't)

Comment thread packages/docs-gesture-handler/docs/gestures/_shared/base-gesture-config.mdx Outdated
This option is configured per gesture handler (for example in `usePanGesture({ cancelsJSResponder: ... })`).

- `true` (default): when this gesture activates, it cancels React Native JS responders in the same root view.
- `false`: disables that cancellation, so Gesture Handler callbacks and RN responder callbacks can run at the same time.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this PR is not really about docs, but imo this one would be hard to read. If you quickly look at false to check what it does you see "disables that cancellation (...)" - I'd change it to be more specific.

Also we tend to be more descriptive, e.g.:

Setting this property to true ensures that the Animated API functions correctly when useNativeDriver is set to false. The default value is set to false.

So maybe something like this would work?

Setting this property to true ensures that the gesture handler cancels any active React Native JS responders in the same root view upon activation. When set to false, both Gesture Handler and React Native responder callbacks can run simultaneously. The default value is set to true.

@coado coado force-pushed the prevent-recognizers branch from 3a3d592 to 9f24c36 Compare April 24, 2026 14:30
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.

5 participants