Skip to content

Add max-content support for width, height, and flex-basis - #2012

Open
intergalacticspacehighway wants to merge 1 commit into
react:mainfrom
intergalacticspacehighway:css-max-content-dimensions
Open

Add max-content support for width, height, and flex-basis#2012
intergalacticspacehighway wants to merge 1 commit into
react:mainfrom
intergalacticspacehighway:css-max-content-dimensions

Conversation

@intergalacticspacehighway

@intergalacticspacehighway intergalacticspacehighway commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Why

Implements the CSS max-content sizing keyword for width, height, and flex-basis.

The public setter (YGNodeStyleSetWidthMaxContent) already exist, but layout silently treated the keyword as auto. This PR makes them take effect.

We have a feature in Expo UI (RNHostView) that allows mounting a React Native hierarchy inside a SwiftUI hierarchy and allowing it to grow to it's natural size. So max-content would be very useful in that case (auto sizes are context dependent and often depend on parent dimensions)

How

The max-content allows content to be laid out to it's intrinsic size, given an indefinite space in an axis. It requires changes to root, flex-basis, cross-axis (prevent stretch) and absolute layout path.

Testing

Test includes browser generated max-content spec which aims to test all the control flow that is added in this PR and also some tests (specifying max-content to owner size) which is not supported by gentest yet.

TODOs

  • min-width: max-content, max-width: max-content not supported yet.
  • Aspect-ratio doesn't transfer from a resolved max-content axis yet (browsers do this). Documented in the disabled max_content_with_aspect_ratio fixture case and pinned by MaxContentAspectRatioTest. Currently aspect ratio needs some work to make it spec compliant, it has the same issue when child has percentage dimensions.
@meta-cla meta-cla Bot added the CLA Signed label Aug 27, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Aug 27, 2026
Implements the CSS max-content sizing keyword (css-sizing-3) for
dimensions and flex-basis. The public setters existed but layout
treated the keyword as auto; this makes them take effect.

Keyword axes are measured with indefinite available space, then laid
out at the measured (min/max clamped) size, so descendants resolve
percentages against the resolved value and wrap within it. Covers
root constraint minting (including a second height measure when a
width clamp changes wrapping), the flex-basis cascade, the cross-axis
path, the stretch re-layout guards, and absolute layout.

Aspect-ratio transfer from a resolved keyword axis is deferred; the
browser target is recorded in a disabled fixture case and the current
behavior is pinned by MaxContentAspectRatioTest. Root cases that need
a definite owner size are hand-written (gentest cannot express one).
intergalacticspacehighway added a commit to expo/expo that referenced this pull request Aug 31, 2026
# Why

Fixes - #47883,
#48059

Fixes the layout loops that happens in some cases when `Host` and
`RNHostView` has `matchContents` and child of `RNHostView` depends upon
parent on sizing. Explained it in more detail here -
#48059 (comment)

<!--
Please describe the motivation for this PR, and link to relevant GitHub
issues, forums posts, or feature requests.
-->

# How

Set `RNHostView` node as a leaf and measurable node using shadow node
traits. This removes it from the yoga layout hierarchy (shadow node
hierarchy is still preserved). Override `measureContent` and `layout`
shadow node functions for `RNHostView`. The `measureContent` is called
by Yoga during layout and `layout` is called during commit.

A better fix would be if Yoga supports `max-content` so we could just do
`<RNHostView style={{ width: "max-content", height: "max-content" }} />`
if someone passes `matchContents`. Made a PR in Yoga for that
react/yoga#2012

<!--
How did you build this feature or fix this bug and why?
-->

# Test Plan

Added regression testcases for RNHostView which leads to loops.



<!--
Please describe how you tested this change and how a reviewer could
reproduce your test, especially if this PR does not include automated
tests! If possible, please also provide terminal output and/or
screenshots demonstrating your test/reproduction.
-->

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
intergalacticspacehighway added a commit to expo/expo that referenced this pull request Aug 31, 2026
Fixes - #47883,
#48059

Fixes the layout loops that happens in some cases when `Host` and
`RNHostView` has `matchContents` and child of `RNHostView` depends upon
parent on sizing. Explained it in more detail here -
#48059 (comment)

<!--
Please describe the motivation for this PR, and link to relevant GitHub
issues, forums posts, or feature requests.
-->

Set `RNHostView` node as a leaf and measurable node using shadow node
traits. This removes it from the yoga layout hierarchy (shadow node
hierarchy is still preserved). Override `measureContent` and `layout`
shadow node functions for `RNHostView`. The `measureContent` is called
by Yoga during layout and `layout` is called during commit.

A better fix would be if Yoga supports `max-content` so we could just do
`<RNHostView style={{ width: "max-content", height: "max-content" }} />`
if someone passes `matchContents`. Made a PR in Yoga for that
react/yoga#2012

<!--
How did you build this feature or fix this bug and why?
-->

Added regression testcases for RNHostView which leads to loops.

<!--
Please describe how you tested this change and how a reviewer could
reproduce your test, especially if this PR does not include automated
tests! If possible, please also provide terminal output and/or
screenshots demonstrating your test/reproduction.
-->

<!--
Please check the appropriate items below if they apply to your diff.
-->

- [ ] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)

Backport notes:
- The `apps/test-suite` regression tests are omitted. On sdk-57 `apps/test-suite` has no
  `@expo/ui` dependency and its `types.ts` lacks `JasmineInterface` and `TestPortal`, so the
  new suite needs two unrelated prerequisite commits plus a lockfile change. The native fix,
  the docs note and the changelog entries are backported as-is.
- The `docs/pages/versions/v57.0.0/sdk/ui/**/rnhostview.mdx` hunks are dropped. Those files do
  not exist on sdk-57; `docs/pages/versions/unversioned` is the live SDK 57 docs here.
- Rebuilt `packages/expo-ui/build` because `build/` is tracked on release branches.

(cherry picked from commit 97baf3b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

1 participant