Skip to content

feat: support React 19 bigint, and deprecate fragment flattening#4249

Open
amanabiy wants to merge 3 commits intomainfrom
dev-v3-amanabiy-flatten-fragments-dep
Open

feat: support React 19 bigint, and deprecate fragment flattening#4249
amanabiy wants to merge 3 commits intomainfrom
dev-v3-amanabiy-flatten-fragments-dep

Conversation

@amanabiy
Copy link
Member

@amanabiy amanabiy commented Feb 13, 2026

Description

Cloudscape components (ColumnLayout, Grid, and SpaceBetween) currently flatten React Fragments using react-keyed-flatten-children (v2.2.1), which depends on react-is (^18.2.0). React 19 renamed internal type elements, breaking fragment detection and flattening when using older versions of react-is.

Changes

  • React 16-18: Uses react-keyed-flatten-children for backward compatibility (fragments still flattened)
  • React 19+: Uses Children.toArray() directly (fragments NOT flattened, BigInt support added)

Tested by

  • using our column-layout in different versions of react from 16 - 19 application locally.
  • A simple sandbox test showing toArray behaves as we expect in react 19

Related links, issue #, if available: PptYApl47Z5P

How has this been tested?

Review checklist

The following items are to be evaluated by the author(s) and the reviewer(s).

Correctness

  • Changes include appropriate documentation updates.
  • Changes are backward-compatible if not indicated, see CONTRIBUTING.md.
  • Changes do not include unsupported browser features, see CONTRIBUTING.md.
  • Changes were manually tested for accessibility, see accessibility guidelines.

Security

Testing

  • Changes are covered with new/existing unit tests?
  • Changes are covered with new/existing integration tests?

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@codecov
Copy link

codecov bot commented Feb 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.19%. Comparing base (9fc78ce) to head (6f56617).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4249   +/-   ##
=======================================
  Coverage   97.19%   97.19%           
=======================================
  Files         886      887    +1     
  Lines       26021    26038   +17     
  Branches     9433     9439    +6     
=======================================
+ Hits        25291    25308   +17     
  Misses        724      724           
  Partials        6        6           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@amanabiy amanabiy marked this pull request as ready for review February 13, 2026 12:17
@amanabiy amanabiy requested a review from a team as a code owner February 13, 2026 12:17
@amanabiy amanabiy requested review from jperals and removed request for a team February 13, 2026 12:17
import flattenChildrenLegacy from 'react-keyed-flatten-children';

export function flattenChildren(children: ReactNode): ReactNode[] {
const majorVersion = parseInt(React.version.split('.')[0], 10);
Copy link
Member

Choose a reason for hiding this comment

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

Since React.version is undocumented, should we make this line more failproof? And if checking the version fails, then fall back to the new behavior (i.e, not handling fragments, same as version >= 19)

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, I will do it like that.

{flattenedChildren.map(child => {
const key = typeof child === 'object' ? child.key : undefined;
// If this react child is a primitive value, the key will be undefined
const key = (child as Record<'key', unknown>).key;
Copy link
Member

Choose a reason for hiding this comment

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

Is there a possible case where this will throw an error? Let's say some conditional rendering logic where a child can be rendered as undefined or a number.

Copy link
Member Author

Choose a reason for hiding this comment

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

if the child is a number, bigint (in react 19), or string the key will be undefined. so we will be assigning undefined key. But it shouldn't throw an error.

Copy link
Member

Choose a reason for hiding this comment

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

And what about undefined?

Before the changes, the code was safe because typeof undefined is not 'object', therefore we render undefined according to the ternary operator, but after the changes, we will try to access a property of undefined, which throws an error.

Copy link
Member Author

@amanabiy amanabiy Feb 13, 2026

Choose a reason for hiding this comment

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

You're right that accessing a property on undefined would throw an error. However, this is safe because both Children.toArray() and react-keyed-flatten-children filter out undefined, null, and boolean values.

Looking at the type signature of toArray it returns the following: toArray(children: ReactNode | ReactNode[]): Array<Exclude<ReactNode, boolean | null | undefined>>; The return type explicitly excludes undefined, null, and boolean. React's Children.toArray() implementation (test example). Under the hood, react-keyed-flatten-children also uses Children.toArray so they are filtered automatically, in both cases.

Here is another place where we also use similar code (line 85). That said, if you'd prefer more safe approach, we could add a guard and/or I can change the other implementation as well. What do you think?

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.

2 participants