Skip to content

Conversation

@Nitin-100
Copy link
Contributor

@Nitin-100 Nitin-100 commented Nov 9, 2025

Desciption

On iOS/Android, overflow: 'hidden' clips child content to the parent view's bounds. On React Native Windows (Fabric), this was not working — children always rendered beyond their parent regardless of the overflow property.

Additionally, shadow (shadowColor, shadowOffset, etc.) was being clipped by the border-radius clip on Visual(), making shadows invisible on rounded-corner views.

How

m_childrenContainer — on-demand child clipping visual

A dedicated m_childrenContainer SpriteVisual is created lazily (only when getClipsContentToBounds() is true) as a child of m_visual. Children are mounted into it and clipped via a D2D rounded-rect path geometry.

Visual tree with overflow: hidden:

m_outerVisual          ← shadow applied here (not clipped)
  └── m_visual         ← background, border-radius clip, opacity
        ├── Border × N
        └── m_childrenContainer  ← created on demand, clipped to bounds
              └── <children>

Visual tree with overflow: visible (default):

m_outerVisual
  └── m_visual
        ├── Border × N
        └── <children>           ← mounted directly, no extra visual

Shadow on OuterVisual()

Shadow props are now applied to OuterVisual() instead of Visual(). This prevents the border-radius clip (on Visual()) from clipping the shadow.

RelativeSizeWithOffset for container sizing

m_childrenContainer uses RelativeSizeWithOffset({0,0}, {1,1}) so it automatically tracks the parent's size without manual size updates.

Files Changed

File Change
CompositionViewComponentView.cpp updateChildrenClippingPath(), VisualToMountChildrenInto(), child mount/unmount routing, applyShadowProps()OuterVisual(), updateClippingPath() simplified to border-radius only
CompositionViewComponentView.h Added m_childrenContainer, updateChildrenClippingPath() virtual method
ScrollViewComponentView.cpp Override updateChildrenClippingPath() as no-op (scroll view clips via m_scrollVisual)
ScrollViewComponentView.h Declare the override

Key Design Decisions

  1. Lazy creationm_childrenContainer only created when overflow: hidden/scroll. Views with overflow: visible (default) have zero overhead.

  2. Child migration — When container is created, existing child visuals are moved from m_visual into m_childrenContainer. New children route through VisualToMountChildrenInto().

  3. Border offset skip — When children are in m_childrenContainer, indexOffsetForBorder() is skipped (children start at index 0 in the container). Applied only when children are directly in m_visual.

  4. Once created, never destroyed — If overflow toggles from hidden→visible, m_childrenContainer stays but its clipping path is set to nullptr. Avoids complex child migration back.

  5. Outer border radii for clip geometry — Matches iOS behavior. Clip path uses resolveAndAlignBorderMetrics + GenerateRoundedRectPathGeometry.

  6. ScrollView no-opScrollViewComponentView overrides updateChildrenClippingPath() as no-op because it mounts children into m_scrollVisual which inherently clips.

Testing

  • overflow: hidden clips children at parent bounds (rect + rounded)
  • overflow: visible allows children to extend beyond bounds
  • Shadow visible around views with overflow: hidden
  • Nested overflow (parent hidden, child visible → parent clips)
  • Dynamic toggle between hidden/visible at runtime
  • Transform + overflow interaction
  • ScrollView unaffected (no-op override)

Changelog

react-native-windowsAdded

Implemented overflow: hidden support for Fabric Composition renderer. Views with overflow: 'hidden' now clip children to bounds, matching iOS/Android. Shadow rendering moved to OuterVisual to prevent border-radius clipping.

@Nitin-100 Nitin-100 requested a review from a team as a code owner November 9, 2025 12:44
@HariniMalothu17
Copy link
Contributor

@Nitin-100
Based on the screenshot u shared there is no visual indication here that the 'overflow' prop is actually implemented
Please check the screenshot section in this link
#14527
image

@Nitin-100 Nitin-100 force-pushed the nitin/parity-fabric/overflow branch from 5603646 to 8527924 Compare November 10, 2025 07:14
@Nitin-100 Nitin-100 force-pushed the nitin/parity-fabric/overflow branch from 8527924 to 85b860f Compare November 10, 2025 07:18
@Nitin-100
Copy link
Contributor Author

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@acoates-ms
Copy link
Contributor

We already use a clip for rounded corners to clip the background: ComponentView::updateClippingPath.

Really we need to stop doing that for rounded corners, which means fixing our background logic for rounded corners so that that clip is not required. Then we can do this change.

Copy link
Contributor

@acoates-ms acoates-ms left a comment

Choose a reason for hiding this comment

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

Need to work out how to not conflict with the existing clip.

@microsoft-github-policy-service microsoft-github-policy-service bot added the Needs: Author Feedback The issue/PR needs activity from its author (label drives bot activity) label Nov 10, 2025
@microsoft-github-policy-service microsoft-github-policy-service bot removed the Needs: Author Feedback The issue/PR needs activity from its author (label drives bot activity) label Nov 16, 2025
@anupriya13
Copy link
Contributor

Looks like the prop is not working as expected as per the screenshots attached. Please add E2ETestApp unit tests as well and more examples in playground.

@anupriya13 anupriya13 self-requested a review December 1, 2025 04:22
@Nitin-100
Copy link
Contributor Author

Looks like the prop is not working as expected as per the screenshots attached. Please add E2ETestApp unit tests as well and more examples in playground.

Changes are not added yet, I'm busy with release work.

layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor});

// Apply overflow clipping
if (m_props && m_props->yogaStyle.overflow() == facebook::yoga::Overflow::Hidden) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What about other overflow types?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

getClipsContentToBounds check this

winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerVisual(
Visual());
if (compVisual) {
compVisual.Clip(nullptr);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is as good as doing nothing, right?
If no clip was ever applied, this line is redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Re-review the code.

@microsoft-github-policy-service microsoft-github-policy-service bot added the Needs: Author Feedback The issue/PR needs activity from its author (label drives bot activity) label Dec 1, 2025
@anupriya13
Copy link
Contributor

Looks like the prop is not working as expected as per the screenshots attached. Please add E2ETestApp unit tests as well and more examples in playground.

Changes are not added yet, I'm busy with release work.

Sure, please convert this PR to draft while you work on it.

@microsoft-github-policy-service microsoft-github-policy-service bot removed the Needs: Author Feedback The issue/PR needs activity from its author (label drives bot activity) label Dec 1, 2025
Nitin-100 and others added 3 commits December 1, 2025 10:53
Hi OSG Instrumentation Team,

I'm working on React Native Windows telemetry. We currently have:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
EXISTING (working):
- JavaScript CLI telemetry via 1DS SDK
- Instrumentation Key: 49ff6d3ef12f4578a7b75a2573d9dba8-026332b2-2d50-452f-ad0d-50f921c97a9d-7145
- Data flows to: Aria → Kusto
- Kusto cluster: [YOU NEED TO TELL ME THIS]

NEW (want to add):
- Native C++ telemetry via ETW/TraceLogging
- Provider Name: Microsoft.ReactNativeWindows.Telemetry
- Provider GUID: [WILL GENERATE]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

QUESTION:
Can you register the new ETW provider GUID and route it to the SAME
Aria tenant as our existing 1DS instrumentation key?

Goal: Have both JavaScript (1DS) and C++ (ETW) telemetry appear in
the same Kusto database for unified querying.

Is this possible? If yes, what info do you need from me?

Thanks,
Harini Malothu
React Native Windows Team
@Nitin-100
Copy link
Contributor Author

Need to work out how to not conflict with the existing clip.

@acoates-ms Please re review it as I have handled all scenarios now.

Copy link
Contributor

@sundaramramaswamy sundaramramaswamy left a comment

Choose a reason for hiding this comment

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

LGTM.

@Nitin-100
Copy link
Contributor Author

Nitin-100 commented Feb 2, 2026

@acoates-ms
Changes Done:
Split ensureVisual() into ensureVisual() and ensureContentVisual()
m_contentVisual is now only created when:
Children need to be mounted - MountChildComponentView() calls ensureContentVisual()
overflow:hidden is set - updateLayoutMetrics() checks getClipsContentToBounds() and creates m_contentVisual only if needed

@Nitin-100 Nitin-100 closed this Feb 3, 2026
@Nitin-100 Nitin-100 reopened this Feb 3, 2026
Nitin-100 and others added 9 commits February 3, 2026 10:42
…idden

- Remove ensureContentVisual() calls from VisualToMountChildrenInto() and MountChildComponentView()
- m_contentVisual is now only created in updateLayoutMetrics when getClipsContentToBounds() is true
- Add null check in onThemeChanged() before accessing Visual()
- Restore original ensureVisual() call in MountChildComponentView()
…ontainer

Implement proper overflow:hidden clipping for the Fabric Composition renderer,
matching iOS/Android behavior. Instead of clipping m_visual directly (which
also clips borders and background), a dedicated m_childrenContainer visual is
created on-demand as a child of m_visual, and only children are clipped.

Visual Tree (with overflow:hidden):
  m_outerVisual
    m_visual (background, opacity, transform, border-radius clip)
      Border Visuals x N
      m_childrenContainer (created on demand, clipped to bounds)
        <children>

Key design decisions:
- m_childrenContainer is created lazily only when overflow:hidden is set
- Existing children are moved from m_visual into m_childrenContainer
- Once created, m_childrenContainer is never destroyed (clip is just removed
  if overflow changes back to visible)
- Clipping uses outer border radii via D2D path geometry, matching iOS
- ScrollView overrides updateChildrenClippingPath as no-op since it manages
  its own visual tree via m_scrollVisual and inherently clips content

Also cleans up:
- Removed unnecessary null-checks on Visual() (ensureVisual guarantees it)
- Removed shadow mask workaround code (not related to overflow)
- Removed InsetClip-based overflow handling from updateProps
- Added assert(m_visual) in Visual() accessor
- Simplified updateClippingPath to only handle border-radius clipping
@Nitin-100 Nitin-100 merged commit 56810ff into microsoft:main Feb 12, 2026
30 checks passed
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.

6 participants