Skip to content

[iOS] Modal supportedOrientations can allow orientations it doesn't list (codegen mask props are parsed into an uninitialized value) #59561

Open

Description

@Neilos

Description

On the New Architecture, a <Modal> can allow orientations that its supportedOrientations prop doesn't list. In our app, modals with supportedOrientations={['landscape-left', 'landscape-right']} came out allowing portrait as well, and turned portrait when the iPad was held upright.

The prop is parsed into an uninitialized value:

  1. convertRawProp declares T result; and hands it to fromRawValue (propsConversions.h#L191).
  2. For supportedOrientations, T is the generated ModalHostViewSupportedOrientationsMaskWrapped, whose only member has no initializer (GeneratePropsH.js#L274-L276). So T result; leaves result.value indeterminate.
  3. The generated fromRawValue ORs each listed orientation into it with wrapped.value |= … (GeneratePropsH.js#L366), so any bits already in that memory stay in the mask.
  4. RCTModalHostViewComponentView allows every orientation whose bit is set (RCTModalHostViewComponentView.mm#L23-L45, applied at #L290).

Whether a build shows it depends on what is on the stack at that point. In our Release build it was consistent: landscape-only modals also allowed portrait. Apps whose Info.plist lists only landscape won't notice, because that caps every modal. It appeared for us once we listed all four orientations so that one particular modal could rotate.

Any codegen array-of-enum (mask) prop goes through the same struct and convertRawProp, so library components with such props are affected too. The std::optional<T> overload of fromRawValue has the same pattern (T resultValue;, propsConversions.h#L118).

Either change would fix it:

  • value-initialize in convertRawProp (and the optional overload): T result{};
  • give the generated member an initializer: ${enumMask} value{};

We've worked around it in our app by capping orientations in application:supportedInterfaceOrientationsForWindow:.

Steps to reproduce

  1. On iOS with the New Architecture, list all four orientations under UISupportedInterfaceOrientations in Info.plist.

  2. Render a landscape-only modal:

    <Modal visible supportedOrientations={['landscape-left', 'landscape-right']}>
      <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
        <Text>Landscape only</Text>
      </View>
    </Modal>
  3. Run on an iPad and hold it upright while the modal is shown. We used a Release build on the iPad mini (A17 Pro) simulator, iPadOS 26.5.

  4. Expected: the modal stays landscape. Actual: UIKit is given portrait as a supported orientation, and the modal turns portrait (log below).

Because the starting value is uninitialized, another project or build may happen to start from zero and not show it. Logging newProps.supportedOrientations in RCTModalHostViewComponentView's updateProps: shows the parsed mask directly.

React Native Version

0.87.1. The same code is in 0.88.1 and on main (eaf4c26).

Affected Platforms

Runtime - iOS

Areas

Fabric - The New Renderer, Codegen

Output of npx @react-native-community/cli info

System:
  OS: macOS 26.5.1
  CPU: (14) arm64 Apple M4 Pro
  Memory: 654.38 MB / 24.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 24.16.0
    path: ~/.local/share/mise/installs/node/24.16.0/bin/node
  Yarn:
    version: 1.22.22
    path: ~/.local/share/mise/installs/yarn/1/bin/yarn
  npm:
    version: 11.13.0
    path: ~/.local/share/mise/installs/node/24.16.0/bin/npm
  Watchman:
    version: 2026.05.18.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.17.0
    path: ~/.local/share/mise/installs/ruby/3.4.10/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 25.5
      - iOS 26.5
      - macOS 26.5
      - tvOS 26.5
      - visionOS 26.5
      - watchOS 26.5
  Android SDK: Not Found
IDEs:
  Android Studio: Not Found
  Xcode:
    version: 26.6/17F113
    path: /usr/bin/xcodebuild
Languages:
  Java: Not Found
  Ruby:
    version: 3.4.10
    path: ~/.local/share/mise/installs/ruby/3.4.10/bin/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 20.2.0
    wanted: ^20.2.0
  react:
    installed: 19.2.3
    wanted: 19.2.3
  react-native:
    installed: 0.87.1
    wanted: ^0.87.1
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: Not found
  newArchEnabled: Not found
iOS:
  hermesEnabled: true
  newArchEnabled: true

Stacktrace or Logs

UIKit's orientation log on the simulator, filtered from `log stream`, while a modal with
supportedOrientations={['landscape-left', 'landscape-right']} was presented and the iPad
was held upright. UIKit writes portrait as Pu and landscape left and right as Ll and Lr.

17:07:50.968 Scene updated orientation preferences: ( Lr ) -> ( Pu Ll Lr )
17:07:57.570 Did set interfaceOrientation[1]

interfaceOrientation 1 is UIInterfaceOrientationPortrait.

MANDATORY Reproducer

No standalone reproducer yet. The starting value is uninitialized, so whether a fresh project shows extra orientations depends on what happens to be on the stack. The defect can be read from the lines linked above.

Screenshots and Videos

None.

Activity

  1. react-native-bot commented on Oct 27, 2026

    @react-native-bot
    Collaborator

    Warning

    Missing reproducer: We could not detect a reproducible example in your issue report. Reproducers are mandatory and we can accept only one of those as a valid reproducer:


    You can read more about about it on our website: How to report a bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs: Author FeedbackNeeds: ReproThis issue could be improved with a clear list of steps to reproduce the issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions