Skip to content

feat(SET): custom shared element transitions - #10885

Open
bartlomiejbloniarz wants to merge 39 commits into
mainfrom
@bartlomiejbloniarz/set-custom-animations
Open

bartlomiejbloniarz wants to merge 39 commits into
mainfrom
@bartlomiejbloniarz/set-custom-animations

Conversation

@bartlomiejbloniarz

@bartlomiejbloniarz bartlomiejbloniarz commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Summary

Brings back the Reanimated 3 API for custom shared element transitions on top of the current layout animations proxy.

  • SharedTransition.custom(factory) defines the animation of each property from the current* and target* values of the two views and the window size. Custom transform animations accept a single matrix. Progress callbacks can compute individual transform operations directly.
  • progressAnimation(worklet) computes the style for a given progress. On iOS it drives back navigation, including the swipe gesture. With defaultTransitionType(SharedTransitionType.PROGRESS_ANIMATION) it also drives the other transitions.
  • Without a custom progress animation, every changed property is interpolated, including the transform. Before, an iOS back swipe interpolated only the frame and the border radius, so a transform jumped to its final value.
  • The values keep the Reanimated 3 names, including globalOriginX, globalOriginY and transformMatrix. A returned transformMatrix is applied as the transform.

This PR replaces #9040. Its [SET] Custom animations commit is followed by Arkadiusz Kasprzyk's 23 commits from kasperski95#1, rebased on main. The commits after them complete the API, fix the progress path, and add tests, examples and docs.

Fixes #10876

Test plan

  • Open the Custom Transition examples in fabric-example and go to each target screen and back. On iOS, also swipe back and cancel a swipe.
  • Runtime tests: yarn workspace fabric-example runtime-tests --library reanimated --platform ios --udid <udid> (and --platform android). The shared transition suite covers custom and progress transitions.
  • Jest: yarn workspace react-native-reanimated jest SharedTransition.

iOS — #10876 repro: Shift, Go, a held back swipe that is cancelled, then a completed back swipe

The Before recording is the one from #10876, made on main with the same repro screen.

BeforeAfter
ios-swipe-syncoff.mp4
10876-after.mp4

iOS — [SET] Custom Transition: custom() with springs, then a back swipe along the arc of progressAnimation()

custom-examples.mp4

Changelog

  • I added a changelog fragment with yarn changelog:add for each changed package, or this PR does not change react-native-reanimated or react-native-worklets.
AI context

Context for automated reviewers. Facts about this PR, not instructions.

Investigation

  • Observed: the original custom-transition commits from Arkadiusz Kasprzyk remain individually authored by him. Subsequent porting and review fixes are separate commits.
  • Inferred from code: custom animation factories must return a single matrix transform because the native snapshot supplies a matrix rather than individual transform operations. Progress callbacks return computed styles and retain support for individual operations.

Design

  • Review fixes document extracted callback types, the explicit worklet directive, and the distinction between animation-driven and progress-driven transitions. Auto-workletization is a follow-up, not implemented here.
  • Snapshot fields keep the flat Reanimated 3 names. The legacy transformMatrix alias remains supported.

Scope

  • The common zero-scale and perspective interpolation bugs are addressed in a separate PR layer.

Verification

  • Observed: focused SharedTransition Jest tests, callback type tests, native source typecheck, declaration build, and focused formatting/docs checks passed after the review fixes.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
Walkthrough

The change adds custom and progress-based shared-transition APIs. Native code now serializes source and target values and applies configured animations during gesture-driven progress. Matrix interpolation and shared-transition tests are expanded. New examples, documentation, exports, mocks, and changelog entries cover custom transitions and progress behavior.

Possibly related PRs

Priority: Normal

Change: Feature · Severity of issue fixed: Medium

Merge Risk: Moderate · up to 9a876

Custom shared transitions that pass a plain non-worklet function as an animation can fail on device, and the added Jest test does not cover that boundary. Earlier formatting and error-propagation concerns have reported fixes that are not independently confirmed. Resolve or confirm these before merging.

Pre-merge checks: Passed 3 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Out of Scope Changes check Warning Issue #10876 is limited to transform interpolation during iOS back swipes. The PR also adds the full SharedTransition.custom API, PROGRESS_ANIMATION selection, broad snapshot and property support,… Split the custom-transition API, examples, documentation, public exports, and unrelated matrix-rotation changes into separate pull requests, or link active issues that require those changes. Keep this pull request focused on the #10876 prog…
Passed Passed checks: 3 passed
Check name Status Explanation
Title check Passed The title clearly and concisely identifies the main change: custom shared element transitions for SET.
Linked Issues check Passed Issue #10876 requires progress-driven iOS back swipes to interpolate transforms, start at the current view, and restore the source after cancellation. SharedTransitions.cpp now records the progress …
Description check Passed The pull request description clearly matches the changes. It explains the restored custom and progress shared-transition APIs, native implementation, tests, examples, documentation, and the linked iss…
Full details: Linked Issues check

Explanation

Issue #10876 requires progress-driven iOS back swipes to interpolate transforms, start at the current view, and restore the source after cancellation. SharedTransitions.cpp now records the progress configuration, applies it to gesture-driven shared elements with the current transition progress, and restores sources for cancelled transitions. SharedTransition.ts interpolates changed properties and decomposes and interpolates transform matrices. Jest tests cover progress 0, 0.5, and 1, including transform output. Runtime tests cover progress from 0 to 1.

Full details: Description check

Explanation

The pull request description clearly matches the changes. It explains the restored custom and progress shared-transition APIs, native implementation, tests, examples, documentation, and the linked issue fix.

Full details: Out of Scope Changes check

Explanation

Issue #10876 is limited to transform interpolation during iOS back swipes. The PR also adds the full SharedTransition.custom API, PROGRESS_ANIMATION selection, broad snapshot and property support, many custom-transition examples, public exports, documentation, and a separate Y-axis matrix decomposition fix. These changes are not required by #10876 and are not supporting tests or refactoring for that issue.

Resolution

Split the custom-transition API, examples, documentation, public exports, and unrelated matrix-rotation changes into separate pull requests, or link active issues that require those changes. Keep this pull request focused on the #10876 progress-path fix and its tests.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@bartlomiejbloniarz
bartlomiejbloniarz changed the base branch from main to @bartlomiejbloniarz/set-container-first-frame-end October 14, 2026 11:52

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d36a0a4e-555d-4246-8bec-9af28de9c266
📥 Commits

Reviewing files that changed from the base of the PR and between 816fdaf and 683b14f.

📒 Files selected for processing: 41
  • apps/common-app/runtime-tests/reanimated/tests/layoutAnimations/sharedTransition.test.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/ChangeTheme.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition/BackgroundColor.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition/BasicCustom.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition/BorderRadius.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition/GlobalOrigin.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition/Opacity.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition/ProgressAnimation.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition/Transform.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition/TransformMatrix.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition/Window.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition/index.tsx
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/CustomTransition/styles.ts
  • apps/common-app/src/apps/reanimated/examples/SharedElementTransitions/Profiles.tsx
  • apps/common-app/src/apps/reanimated/examples/index.ts
  • docs/docs-reanimated/docs/shared-element-transitions/overview.mdx
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationType.h
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.h
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.h
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/SharedTransitionValuesFactory.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/SharedTransitionValuesFactory.h
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/SharedTransitions.cpp
  • packages/react-native-reanimated/__tests__/SharedTransition.test.ts
  • packages/react-native-reanimated/__typetests__/sharedTransitionTest.tst.ts
  • packages/react-native-reanimated/changelog/shared-transition-custom-animations.feature.md
  • packages/react-native-reanimated/src/UpdateLayoutAnimations.native.ts
  • packages/react-native-reanimated/src/UpdateLayoutAnimations.ts
  • packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx
  • packages/react-native-reanimated/src/animation/utilCommon.ts
  • packages/react-native-reanimated/src/commonTypes.ts
  • packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.native.tsx
  • packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.tsx
  • packages/react-native-reanimated/src/index.ts
  • packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts
  • packages/react-native-reanimated/src/mock.ts
💤 Files with no reviewable changes: 2
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.h
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/PropsDiffer.cpp

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

Comment thread packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts Outdated
@bartlomiejbloniarz
bartlomiejbloniarz force-pushed the @bartlomiejbloniarz/set-custom-animations branch from aad814b to 43a9dca Compare October 14, 2026 12:23

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟠 Major · Format this file before merging. · matrixUtils.tsx:1

packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx:1
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Format this file before merging.

The static-check pipeline reports that this file fails the formatter check during yarn lint:js. Run the repository formatter on this file and commit the result. The pipeline does not identify a more precise offending line.

Source: Pipeline failures


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 4e763106-68c5-4e57-9da2-7f2f3fbcfd92
📥 Commits

Reviewing files that changed from the base of the PR and between 683b14f and aad814b.

📒 Files selected for processing: 3
  • packages/react-native-reanimated/__tests__/SharedTransition.test.ts
  • packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx
  • packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

@bartlomiejbloniarz

Copy link
Copy Markdown
Member Author

@coderabbitai The matrixUtils.tsx format failure is fixed in 43a9dca. yarn lint:js, including the format check, passes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟡 Minor · Preserve exact ±90° Y endpoints during interpolation. · matrixUtils.tsx:453-467

packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx:453-467
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve exact ±90° Y endpoints during interpolation.

When sinRy is exactly 1 or -1, decomposeMatrixIntoMatricesAndAngles can return rx = π or -π for a matrix whose actual X rotation is zero. interpolateMatrix(0, start, stop) and interpolateMatrix(1, start, stop) then rebuild a matrix that differs from the corresponding endpoint. The shared-transition progress path uses this interpolation for transform matrices, so the moving element can start with a transform jump and finish in an incorrect transform state.

Use the matrix entries that remain unambiguous when Y reaches gimbal lock:

Suggested fix
-    rx = Math.atan2(sinRy * rotationMatrix[0][1], sinRy * rotationMatrix[0][2]);
+    rx = Math.atan2(rotationMatrix[2][1], rotationMatrix[1][1]);

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7c6b9ee0-5c97-4ae2-b404-a98689b1cb53
📥 Commits

Reviewing files that changed from the base of the PR and between aad814b and 43a9dca.

📒 Files selected for processing: 1
  • packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@bartlomiejbloniarz Thanks for fixing the format failure in 43a9dca and confirming that yarn lint:js, including the format check, passes.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

bartlomiejbloniarz added a commit that referenced this pull request Oct 14, 2026
At the gimbal lock the X angle was read from the first row of the rotation,
which gives ±π for a rotation around Y alone, so rebuilding the matrix flipped
the Y and Z axes. The X and Z rotations combine there, and the second column
gives their angle.

Addresses #10885 (review)

Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
@bartlomiejbloniarz

Copy link
Copy Markdown
Member Author

@coderabbitai The ±90° Y case was broken: a rotation around Y alone decomposed to rx = ±π and rebuilt with the Y and Z axes flipped. Fixed in 1ed6f8a.

The suggested atan2(R[2][1], R[1][1]) is not right either. It still fails to rebuild the matrix when X or Z is non-zero, because at the gimbal lock R[2][1] = -sin(rx) in this convention. The fix uses atan2(-R[2][1], R[1][1]), which also folds the Z angle into X. A new test in matrixUtils.test.tsx rebuilds rx = 0.4, ry = ±π/2, rz = 0.7 and fails without the change.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
fd '^matrixUtils\.(tsx|test\.tsx)$'
ast-grep outline packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx
ast-grep outline packages/react-native-reanimated/__tests__/matrixUtils.test.tsx
rg -n -C 22 'Math\.atan2|Math\.asin|gimbal' packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx
rg -n -C 25 '0\.4|0\.7|interpolateMatrix|decomposeMatrixIntoMatricesAndAngles' packages/react-native-reanimated/__tests__/matrixUtils.test.tsx

Length of output: 6384


@bartlomiejbloniarz Thanks for the correction. My suggested atan2(R[2][1], R[1][1]) had the wrong sign for this matrix convention. I withdraw that suggestion.

The implementation now uses atan2(-R[2][1], R[1][1]) with rz = 0. The regression test checks matrix reconstruction at both ry = ±π/2, with nonzero X and Z rotations. I verified the code and test by inspection; I did not run the test.


✏️ Learnings added
Learnt from: bartlomiejbloniarz
URL: https://github.com/software-mansion/react-native-reanimated/pull/10885

Timestamp: 2026-10-14T12:51:20.259Z
Learning: In packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx, decomposeMatrixIntoMatricesAndAngles uses the gimbal-lock formula rx = Math.atan2(-rotationMatrix[2][1], rotationMatrix[1][1]) with rz = 0 when sinRy is ±1. The negative sign is required by this matrix convention, and the resulting X angle incorporates the Z rotation. Using the positive-sign formula fails matrix reconstruction with nonzero X or Z rotations. packages/react-native-reanimated/__tests__/matrixUtils.test.tsx covers reconstruction with rx = 0.4, ry = ±π/2, and rz = 0.7.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@bartlomiejbloniarz
bartlomiejbloniarz added this pull request to stack #10888 October 14, 2026 12:54
@bartlomiejbloniarz
bartlomiejbloniarz force-pushed the @bartlomiejbloniarz/set-custom-animations branch from 1ed6f8a to 07d84a9 Compare October 14, 2026 22:02
@bartlomiejbloniarz
bartlomiejbloniarz force-pushed the @bartlomiejbloniarz/set-custom-animations branch from 07d84a9 to 7f972cf Compare October 14, 2026 22:07

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d9710d72-d371-4d52-aff5-6249d82c1bd4
📥 Commits

Reviewing files that changed from the base of the PR and between 1ed6f8a and 7f972cf.

📒 Files selected for processing: 6
  • apps/common-app/runtime-tests/reanimated/tests/layoutAnimations/sharedTransition.test.tsx
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy.h
  • packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/SharedTransitions.cpp
  • packages/react-native-reanimated/src/createAnimatedComponent/AnimatedComponent.native.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.

@bartlomiejbloniarz

Copy link
Copy Markdown
Member Author

@coderabbitai The moderate merge risk in your summary rests on the two findings you withdrew in #10885 (comment) and #10885 (comment). Fabric does not reuse view tags. Progress worklet errors propagate the same way as errors in the custom animation factory that startLayoutAnimation runs. Please update the risk assessment.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

@bartlomiejbloniarz You’re right. The two withdrawn findings do not justify the moderate merge-risk rating.

The earlier clarifications establish that:

  • Fabric does not reuse view tags.
  • Progress worklet errors propagate consistently with custom animation factory errors in startLayoutAnimation.

Revised assessment: low merge risk based on the available review evidence. This assessment does not establish that runtime tests pass.

I’m requesting a summary update to remove both withdrawn findings as risk factors and revise the rating.


🧠 Learnings used
Learnt from: bartlomiejbloniarz
Repo: software-mansion/react-native-reanimated PR: 10885
File: packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/SharedTransitions.cpp:282-287
Timestamp: 2026-10-14T22:18:50.023Z
Learning: In react-native-reanimated's C++ layout animation implementation, startLayoutAnimation invokes user animation factories with a plain JSI call and allows exceptions to propagate. The progress worklet call in LayoutAnimationsProxy::applyProgressAnimations in packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/SharedTransitions.cpp follows this behavior. Error-handling changes should cover all layout animation entry points together rather than silently skip broken worklets only in the progress path.
Learnt from: bartlomiejbloniarz
Repo: software-mansion/react-native-reanimated PR: 10885
File: packages/react-native-reanimated/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.cpp:147-153
Timestamp: 2026-10-14T22:18:44.528Z
Learning: In react-native-reanimated's Fabric shared-transition implementation, sharedTransitions_ and progressSharedTransitions_ in LayoutAnimationsManager have the same lifetime. AnimatedComponent._configureSharedTransition sets and clears both configurations together, and LayoutAnimationsManager::transferConfigFromNativeID transfers both from native IDs to view tags. Fabric does not reuse view tags, so retained configurations cannot be picked up by another view through tag reuse. Any cleanup of retained unmounted-view entries should cover both maps together rather than treating progressSharedTransitions_ as a separate lifecycle defect.
✅ Action performed

Summary regeneration completed, but the summary was unchanged.

@bartlomiejbloniarz
bartlomiejbloniarz marked this pull request as ready for review October 20, 2026 10:16
@bartlomiejbloniarz

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7b4797a6-94a8-4f24-81b8-b2cba5ff3da6
📥 Commits

Reviewing files that changed from the base of the PR and between 7f972cf and 9a876d8.

📒 Files selected for processing: 3
  • apps/common-app/runtime-tests/reanimated/tests/layoutAnimations/sharedTransition.test.tsx
  • packages/react-native-reanimated/__tests__/SharedTransition.test.ts
  • packages/react-native-reanimated/src/layoutReanimation/SharedTransition.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

};
});
```

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 was wondering whether in docs we should also show how to type separate callbacks with the exported CustomSharedTransitionAnimation and ProgressSharedTransitionAnimation types?

Or to mention those types somewhere in API section (if we have such section in docs for SETs).

Just thinking it would make sense for cases where people don't put the callbacks inline?


```tsx
const transition = SharedTransition.custom((values) => {
'worklet';

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.

Do you think it would be possible for us to add auto-workletization for these callbacks in a follow-up PR, with matching scoped to SharedTransition calls?

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.

Until then, I think in some remarks section we should explicitly state that custom and progressAnimation callbacks need the 'worklet' directive. As far as I remember we do it in all the other places in docs where the directive is needed

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 imagine we need to rework the docs anyway once all our joint efforts on finalizing all those PRs are done, not to have just the Overview section but to plan it a bit more thoroughly, so we could as well leave that remark about docs for later. Still curious about auto-workletization though

```

On iOS, when the animation is triggered by a swipe gesture or the OS back button we instead run a progress-based transition. This way the animation responds to the user swiping their finger accordingly, and allows for transition cancellation. This behavior will be possible to customize in the future.
To define the animation yourself, use `SharedTransition.custom`. The worklet receives the values of the source view (`current*`), the values of the target view (`target*`) and the window size (`windowWidth`, `windowHeight`), and returns the animations for each property.

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.

Not necessarily a change I would make, as I don't have a strong opinion, but just curious: have we considered here ever to have two separate target and current objects inside values to be able to have target.width instead of targetWidth?

Comment on lines +26 to +38
export enum SharedTransitionType {
/**
* Use the regular animation for screen transitions and the progress-based
* animation for transitions that follow the screen transition progress (iOS
* back navigation).
*/
ANIMATION = 'animation',
/**
* Use the progress-based animation for all transitions. Screen transitions
* drive the progress with the builder's animation.
*/
PROGRESS_ANIMATION = 'progressAnimation',
}

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.

Maybe it is just me but I found those comments a bit hard to follow. If you agree, perhaps we could try to rephrase some of this.
For example could we clarify what controls progress in each case? I find “screen transitions drive the progress with the builder’s animation” a bit vague without giving a short example

Here is my spin on this, but it's just a draft of what kind of info I'd see here still 🤔

Suggested change
export enum SharedTransitionType {
/**
* Use the regular animation for screen transitions and the progress-based
* animation for transitions that follow the screen transition progress (iOS
* back navigation).
*/
ANIMATION = 'animation',
/**
* Use the progress-based animation for all transitions. Screen transitions
* drive the progress with the builder's animation.
*/
PROGRESS_ANIMATION = 'progressAnimation',
}
export enum SharedTransitionType {
/**
* Use the regular or custom animation, except during iOS back navigation.
* During iOS back navigation, use the progress animation controlled by
* the screen transition.
*/
ANIMATION = 'animation',
/**
* Always use the progress animation.
* During iOS back navigation, the screen transition controls progress.
* Otherwise, the builder settings, such as `.duration()` or `.springify()`,
* control progress.
*/
PROGRESS_ANIMATION = 'progressAnimation',
}

kasperski95 and others added 21 commits October 23, 2026 11:10
…out animations proxy

Register the progress animation from AnimatedComponent.native.tsx, run it from
onTransitionProgress on the UI thread, pass its config with
ProgressLayoutAnimationStart, register progress configs under the progress layout
animation type, resolve transforms against the frame in
SharedTransitionValuesFactory, and register the Custom Transition example.

Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
- Every shared transition registers a progress animation; without a custom one
  every changed property is interpolated (numbers, colors, percentages, arrays,
  objects and the decomposed transform matrix).
- The default and custom animations start from the properties that differ,
  pass the transform as [{ matrix }] and skip the globalOrigin and
  transformMatrix aliases. A custom animation also starts the properties it
  animates from the source view, and follows the builder's delay and reduce
  motion.
- The progress animation registered for the native progress path processes
  its colors, including the box shadow colors.
- PROGRESS_ANIMATION drives the progress animation with the builder's animation
  for transitions that don't follow the screen transition progress.
- Add the static progressAnimation() and defaultTransitionType(), map a returned
  transformMatrix to transform, and type every value the native side passes.
- Global origins equal the window-space origins, and per-corner and per-side
  border values are passed only when one of the views sets them.
- Add a chainable SharedTransition mock.

Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
The native progress path interpolated only the frame and the border radius, so
a transform of the source jumped to its full value at the start of an iOS back
swipe. Every shared view now has a progress animation, so the native linear
interpolation is removed and the progress animation is skipped for containers
whose start is still pending or that are not progress transitions.

Fixes #10876

Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
…vice

Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
The type check with the legacy React Native types rejects numeric colors in a
BoxShadowValue, and lint rejects the empty onStart in the reduce motion test:
https://github.com/software-mansion/react-native-reanimated/actions/runs/38559289163/job/115836086761

Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
…ixUtils

find-unused-code reports scaleMatrix and getRotationMatrix after the matrix
interpolation moved into matrixUtils:
https://github.com/software-mansion/react-native-reanimated/actions/runs/38561505177/job/115843438672

Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
At the gimbal lock the X angle was read from the first row of the rotation,
which gives ±π for a rotation around Y alone, so rebuilding the matrix flipped
the Y and Z axes. The X and Z rotations combine there, and the second column
gives their angle.

Addresses #10885 (review)

Co-authored-by: Arkadiusz Kasprzyk <arkadiusz.kasprzyk@swmansion.com>
@bartlomiejbloniarz
bartlomiejbloniarz force-pushed the @bartlomiejbloniarz/set-custom-animations branch from 9a876d8 to 9b29581 Compare October 23, 2026 09:10

@pawicao pawicao left a comment

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.

Three questions about transform types and interpolation.

*/
export type CustomSharedTransitionAnimation = (
values: SharedTransitionAnimationsValues
) => StyleProps;

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.

Could we restrict the return type for transform to the supported matrix form? It currently accepts transform: [{ scale: withTiming(2) }], but that starts without a matching initial value and produces NaN.

decomposeMatrixIntoMatricesAndAngles(
(current as { matrix: AffineMatrixFlat }).matrix
),
decomposeMatrixIntoMatricesAndAngles(

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.

Could we handle zero-scale matrices before decomposition? With a target scale: 0, this path divides by zero and returns NaN matrix entries. I reproduced this in a Jest test of the default progress animation.

jsi::Array SharedTransitionValuesFactory::buildMatrixArray(const ShadowView &view) {
// Resolving the operations against the frame turns percent translates into points.
const auto transform =
BaseViewProps::resolveTransform(view.layoutMetrics.frame.size, getViewProps(view).transform, TransformOrigin{});

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.

Does the matrix interpolation path preserve perspective? With a target perspective: 500, the default progress animation returns matrix entry 11 as 0 instead of -0.002, even at progress 1. Converting all transforms to matrices also sends regular perspective transforms through this path.

@bartlomiejbloniarz
bartlomiejbloniarz removed this pull request from stack #10888 October 25, 2026 15:51
@bartlomiejbloniarz
bartlomiejbloniarz added this pull request to stack #11009 October 25, 2026 15:51

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SET][iOS] A back swipe applies the transform of the source element in full instead of interpolating it

3 participants