From 37fd56d48092245f4ff3ebad2e8440eed43d7de8 Mon Sep 17 00:00:00 2001 From: Mad Dinh Date: Sun, 23 Aug 2026 08:17:35 +0700 Subject: [PATCH 1/3] fix(CSS): tolerate leading whitespace in a transform string `processTransform` splits on `/\)\s*/`, so whitespace after each `)` is consumed, and `filter(Boolean)` drops the empty part a trailing one leaves behind. Nothing reaches the start of the string, so the first function keeps its padding, `parseTransformProperty` reads the key as `' translate'`, no case matches, and the whole declaration throws: processTransform(' translate(25px) rotate(45deg)') // [Reanimated] Invalid transform property: translate(25px) The same string without the two leading spaces parses fine, and React Native's own parser accepts both. CSS ignores leading whitespace in a property value, and a value built from a template literal or read from a config file easily carries some. Trim the string before splitting. --- .../style/processors/__tests__/transform.test.ts | 10 ++++++++++ .../src/common/style/processors/transform.ts | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/packages/react-native-reanimated/src/common/style/processors/__tests__/transform.test.ts b/packages/react-native-reanimated/src/common/style/processors/__tests__/transform.test.ts index 1986fc7e9140..8169fba9f72c 100644 --- a/packages/react-native-reanimated/src/common/style/processors/__tests__/transform.test.ts +++ b/packages/react-native-reanimated/src/common/style/processors/__tests__/transform.test.ts @@ -287,6 +287,16 @@ describe(processTransform, () => { { rotate: '45deg' }, ], }, + { + // A value read from a template literal can arrive padded; the split + // already tolerates whitespace everywhere except the very start. + input: ' translate(25, 25) scale(2) ', + output: [{ translateX: 25 }, { translateY: 25 }, { scale: 2 }], + }, + { + input: '\n translate(25, 25)\n scale(2)\n', + output: [{ translateX: 25 }, { translateY: 25 }, { scale: 2 }], + }, { input: 'translate(50, 50) scale(1.5, 2) skew(30deg, 15deg)', output: [ diff --git a/packages/react-native-reanimated/src/common/style/processors/transform.ts b/packages/react-native-reanimated/src/common/style/processors/transform.ts index c03cd7d89e54..84b61e2ea513 100644 --- a/packages/react-native-reanimated/src/common/style/processors/transform.ts +++ b/packages/react-native-reanimated/src/common/style/processors/transform.ts @@ -183,7 +183,11 @@ export const processTransform: ValueProcessor = ( return value; } + // The split consumes whitespace after each ')', and `filter(Boolean)` drops + // what a trailing one leaves behind, but the start of the string is only + // reached by trimming it. return value + .trim() .split(/\)\s*/) .filter(Boolean) .flatMap((part) => { From 1336503a6c8960b7d32ab90836dce7dfaabbdc59 Mon Sep 17 00:00:00 2001 From: Mad Dinh Date: Sun, 23 Aug 2026 08:18:11 +0700 Subject: [PATCH 2/3] docs(Reanimated): add changelog entry for the transform whitespace fix --- packages/react-native-reanimated/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-native-reanimated/CHANGELOG.md b/packages/react-native-reanimated/CHANGELOG.md index 9cf209ed1cf8..b14b88d0470f 100644 --- a/packages/react-native-reanimated/CHANGELOG.md +++ b/packages/react-native-reanimated/CHANGELOG.md @@ -12,6 +12,7 @@ ### 🐛 Bug fixes +- Fix a transform string with leading whitespace throwing instead of parsing. ([#10388](https://github.com/software-mansion/react-native-reanimated/pull/10388) by [@dennytosp](https://github.com/dennytosp)) - Fix animated styles on sticky headers throwing an immutable-object mutation error in development. ([#10389](https://github.com/software-mansion/react-native-reanimated/pull/10389) by [@ngocdevv](https://github.com/ngocdevv)) - Fix shared element transitions never running on Android for npm installs by publishing `react-native.config.js`, which registers the Shared Transition Boundary component descriptor for autolinking. ([#10375](https://github.com/software-mansion/react-native-reanimated/pull/10375) by [@dennytosp](https://github.com/dennytosp)) - Keep exiting views at their position in the host tree, so they no longer draw above later siblings. ([#10392](https://github.com/software-mansion/react-native-reanimated/pull/10392) by [@pawicao](https://github.com/pawicao)) From 236434f03abd30d6a076b5ccc0278d67ece96ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81opaci=C5=84ski?= Date: Thu, 27 Aug 2026 01:20:18 +0200 Subject: [PATCH 3/3] Trim transformOrigin strings as well processTransformOrigin splits on /\s+/, so a leading space makes the first component an empty string and the value throws, exactly like the transform string did. Same one-line fix, with cases covering both ends of the value. Drop the comments above the two trims - they restate the code. --- packages/react-native-reanimated/CHANGELOG.md | 2 +- .../style/processors/__tests__/transform.test.ts | 2 -- .../processors/__tests__/transformOrigin.test.ts | 14 ++++++++++++++ .../src/common/style/processors/transform.ts | 3 --- .../src/common/style/processors/transformOrigin.ts | 3 ++- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/react-native-reanimated/CHANGELOG.md b/packages/react-native-reanimated/CHANGELOG.md index bea31aef8160..111a5a821670 100644 --- a/packages/react-native-reanimated/CHANGELOG.md +++ b/packages/react-native-reanimated/CHANGELOG.md @@ -13,7 +13,7 @@ ### 🐛 Bug fixes -- Fix a transform string with leading whitespace throwing instead of parsing. ([#10388](https://github.com/software-mansion/react-native-reanimated/pull/10388) by [@dennytosp](https://github.com/dennytosp)) +- Fix `transform` and `transformOrigin` strings padded with whitespace throwing instead of parsing. ([#10388](https://github.com/software-mansion/react-native-reanimated/pull/10388) by [@dennytosp](https://github.com/dennytosp)) - Fix animated styles on sticky headers throwing an immutable-object mutation error in development. ([#10389](https://github.com/software-mansion/react-native-reanimated/pull/10389) by [@ngocdevv](https://github.com/ngocdevv)) - Fix shared element transitions never running on Android for npm installs by publishing `react-native.config.js`, which registers the Shared Transition Boundary component descriptor for autolinking. ([#10375](https://github.com/software-mansion/react-native-reanimated/pull/10375) by [@dennytosp](https://github.com/dennytosp)) - Keep exiting views at their position in the host tree, so they no longer draw above later siblings. ([#10392](https://github.com/software-mansion/react-native-reanimated/pull/10392) by [@pawicao](https://github.com/pawicao)) diff --git a/packages/react-native-reanimated/src/common/style/processors/__tests__/transform.test.ts b/packages/react-native-reanimated/src/common/style/processors/__tests__/transform.test.ts index 8169fba9f72c..64f8607d8326 100644 --- a/packages/react-native-reanimated/src/common/style/processors/__tests__/transform.test.ts +++ b/packages/react-native-reanimated/src/common/style/processors/__tests__/transform.test.ts @@ -288,8 +288,6 @@ describe(processTransform, () => { ], }, { - // A value read from a template literal can arrive padded; the split - // already tolerates whitespace everywhere except the very start. input: ' translate(25, 25) scale(2) ', output: [{ translateX: 25 }, { translateY: 25 }, { scale: 2 }], }, diff --git a/packages/react-native-reanimated/src/common/style/processors/__tests__/transformOrigin.test.ts b/packages/react-native-reanimated/src/common/style/processors/__tests__/transformOrigin.test.ts index b4c9c4b5c523..5c92a9a97b76 100644 --- a/packages/react-native-reanimated/src/common/style/processors/__tests__/transformOrigin.test.ts +++ b/packages/react-native-reanimated/src/common/style/processors/__tests__/transformOrigin.test.ts @@ -114,6 +114,20 @@ describe(processTransformOrigin, () => { }, ], }, + { + name: 'padded string syntax', + cases: [ + { + name: 'surrounded by whitespace', + cases: [ + { input: ' 50% 50%', output: ['50%', '50%', 0] }, + { input: ' left top ', output: [0, 0, 0] }, + { input: '\n 25px 25% 25px\n', output: [25, '25%', 25] }, + { input: '\tcenter', output: ['50%', '50%', 0] }, + ], + }, + ], + }, { name: 'array syntax', cases: [ diff --git a/packages/react-native-reanimated/src/common/style/processors/transform.ts b/packages/react-native-reanimated/src/common/style/processors/transform.ts index 84b61e2ea513..e59c8212d14f 100644 --- a/packages/react-native-reanimated/src/common/style/processors/transform.ts +++ b/packages/react-native-reanimated/src/common/style/processors/transform.ts @@ -183,9 +183,6 @@ export const processTransform: ValueProcessor = ( return value; } - // The split consumes whitespace after each ')', and `filter(Boolean)` drops - // what a trailing one leaves behind, but the start of the string is only - // reached by trimming it. return value .trim() .split(/\)\s*/) diff --git a/packages/react-native-reanimated/src/common/style/processors/transformOrigin.ts b/packages/react-native-reanimated/src/common/style/processors/transformOrigin.ts index ca55f5e250cb..cae21ac5da5a 100644 --- a/packages/react-native-reanimated/src/common/style/processors/transformOrigin.ts +++ b/packages/react-native-reanimated/src/common/style/processors/transformOrigin.ts @@ -150,7 +150,8 @@ export const processTransformOrigin: ValueProcessor< > = (value) => { 'worklet'; const isArray = Array.isArray(value); - let components = typeof value === 'string' ? value.split(/\s+/) : value; + let components = + typeof value === 'string' ? value.trim().split(/\s+/) : value; const customParse = isArray ? () => null : parsePx; if (components.length < 1 || components.length > 3) {