From 81b5bc26b6b5da88af0197e9be1b4150f4e709c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tar=C4=B1k?= <tarikdotcom@gmail.com>
Date: Mon, 15 Jun 2026 00:10:01 +0200
Subject: [PATCH] Optimize nested style array flattening

---
 .../__tests__/flattenStyle-benchmark-itest.js | 72 +++++++++++++++++++
 .../StyleSheet/__tests__/flattenStyle-test.js | 31 ++++++++
 .../Libraries/StyleSheet/flattenStyle.js      | 41 +++++++----
 3 files changed, 131 insertions(+), 13 deletions(-)
 create mode 100644 packages/react-native/Libraries/StyleSheet/__tests__/flattenStyle-benchmark-itest.js

diff --git a/packages/react-native/Libraries/StyleSheet/__tests__/flattenStyle-benchmark-itest.js b/packages/react-native/Libraries/StyleSheet/__tests__/flattenStyle-benchmark-itest.js
new file mode 100644
index 00000000000..02838801b57
--- /dev/null
+++ b/packages/react-native/Libraries/StyleSheet/__tests__/flattenStyle-benchmark-itest.js
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict-local
+ * @format
+ */
+
+import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
+
+import flattenStyle from '../flattenStyle';
+import * as Fantom from '@react-native/fantom';
+
+const baseStyle = {
+  width: 100,
+  height: 40,
+  opacity: 0.8,
+  backgroundColor: 'blue',
+  borderRadius: 8,
+};
+
+const overrideStyle = {
+  height: 44,
+  opacity: 1,
+  borderWidth: 1,
+  borderColor: 'red',
+};
+
+const accentStyle = {
+  borderRadius: 12,
+  transform: [{scale: 1.1}],
+};
+
+const objectStyle = baseStyle;
+const singleArrayStyle = [baseStyle];
+const singleEffectiveArrayStyle: $FlowFixMe = [
+  null,
+  false,
+  undefined,
+  baseStyle,
+];
+const nestedSingleArrayStyle: $FlowFixMe = [
+  null,
+  [undefined, baseStyle],
+  false,
+];
+const nestedMergedArrayStyle = [baseStyle, [null, overrideStyle, accentStyle]];
+const mergedArrayStyle = [baseStyle, overrideStyle];
+
+Fantom.unstable_benchmark
+  .suite('flattenStyle', {minTestExecutionTimeMs: 500})
+  .test('flatten object style', () => {
+    flattenStyle(objectStyle);
+  })
+  .test('flatten array with one style', () => {
+    flattenStyle(singleArrayStyle);
+  })
+  .test('flatten array with one effective style', () => {
+    flattenStyle(singleEffectiveArrayStyle);
+  })
+  .test('flatten nested array with one effective style', () => {
+    flattenStyle(nestedSingleArrayStyle);
+  })
+  .test('flatten nested array with merged styles', () => {
+    // $FlowFixMe[incompatible-call]
+    flattenStyle(nestedMergedArrayStyle);
+  })
+  .test('flatten array with merged styles', () => {
+    flattenStyle(mergedArrayStyle);
+  });
diff --git a/packages/react-native/Libraries/StyleSheet/__tests__/flattenStyle-test.js b/packages/react-native/Libraries/StyleSheet/__tests__/flattenStyle-test.js
index 3d42d302dab..e380443585b 100644
--- a/packages/react-native/Libraries/StyleSheet/__tests__/flattenStyle-test.js
+++ b/packages/react-native/Libraries/StyleSheet/__tests__/flattenStyle-test.js
@@ -94,6 +94,37 @@ describe('flattenStyle', () => {
     });
   });
 
+  it('should allocate an object when an array contains one style', () => {
+    const style = {a: 'b'};
+    const singleStyle = flattenStyle([style]);
+
+    expect(singleStyle).not.toBe(style);
+    expect(singleStyle).toEqual(style);
+  });
+
+  it('should allocate an object when an array contains one effective style', () => {
+    const style = {a: 'b'};
+    const singleStyle = flattenStyle([null, false, undefined, style]);
+    const singleStyleAgain = flattenStyle([null, [undefined, style], false]);
+
+    expect(singleStyle).not.toBe(style);
+    expect(singleStyleAgain).not.toBe(style);
+    expect(singleStyle).toEqual(style);
+    expect(singleStyleAgain).toEqual(style);
+  });
+
+  it('should allocate an object when merging multiple styles', () => {
+    const style1 = {width: 10};
+    const style2 = {height: 20};
+    const flatStyle = flattenStyle([style1, style2]);
+
+    expect(flatStyle).not.toBe(style1);
+    expect(flatStyle).not.toBe(style2);
+    expect(style1).toEqual({width: 10});
+    expect(style2).toEqual({height: 20});
+    expect(flatStyle).toEqual({width: 10, height: 20});
+  });
+
   it('should merge single class and style properly', () => {
     const fixture = getFixture();
     const style = {styleA: 'overrideA', styleC: 'overrideC'};
diff --git a/packages/react-native/Libraries/StyleSheet/flattenStyle.js b/packages/react-native/Libraries/StyleSheet/flattenStyle.js
index 5d787128aa4..0b217c126b1 100644
--- a/packages/react-native/Libraries/StyleSheet/flattenStyle.js
+++ b/packages/react-native/Libraries/StyleSheet/flattenStyle.js
@@ -20,6 +20,31 @@ type NonAnimatedNodeObject<TStyleProp> = TStyleProp extends AnimatedNode
   ? empty
   : TStyleProp;
 
+function flattenStyleArrayInto<
+  TStyleProp extends ____DangerouslyImpreciseAnimatedStyleProp_Internal,
+>(result: {[string]: $FlowFixMe}, styles: ReadonlyArray<?TStyleProp>) {
+  for (let i = 0, styleLength = styles.length; i < styleLength; ++i) {
+    const style = styles[i];
+    if (style === null || typeof style !== 'object') {
+      continue;
+    }
+
+    if (Array.isArray(style)) {
+      // $FlowFixMe[underconstrained-implicit-instantiation]
+      flattenStyleArrayInto(result, style);
+      continue;
+    }
+
+    // $FlowFixMe[invalid-in-rhs]
+    for (const key in style) {
+      // $FlowFixMe[incompatible-use]
+      // $FlowFixMe[invalid-computed-prop]
+      // $FlowFixMe[prop-missing]
+      result[key] = style[key];
+    }
+  }
+}
+
 function flattenStyle<
   TStyleProp extends ____DangerouslyImpreciseAnimatedStyleProp_Internal,
 >(
@@ -36,19 +61,9 @@ function flattenStyle<
   }
 
   const result: {[string]: $FlowFixMe} = {};
-  for (let i = 0, styleLength = style.length; i < styleLength; ++i) {
-    // $FlowFixMe[underconstrained-implicit-instantiation]
-    const computedStyle = flattenStyle(style[i]);
-    if (computedStyle) {
-      // $FlowFixMe[invalid-in-rhs]
-      for (const key in computedStyle) {
-        // $FlowFixMe[incompatible-use]
-        // $FlowFixMe[invalid-computed-prop]
-        // $FlowFixMe[prop-missing]
-        result[key] = computedStyle[key];
-      }
-    }
-  }
+  // $FlowFixMe[underconstrained-implicit-instantiation]
+  flattenStyleArrayInto(result, style);
+
   // $FlowFixMe[incompatible-type]
   return result;
 }
