From e660af38e4f68aeda9b2486c33732dd6d2a2caab Mon Sep 17 00:00:00 2001
From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com>
Date: Sun, 10 May 2026 11:44:32 -0700
Subject: [PATCH 1/5] Add hideTitleBar and hideBorder to Modal
---
.../Modal/ModalPresentation.windows.js | 36 +++++++-
.../WindowsModalHostViewComponentView.cpp | 82 ++++++++++++++++---
.../src-win/Libraries/Modal/Modal.windows.js | 2 +
.../RCTModalHostViewNativeComponent.js | 4 +
4 files changed, 110 insertions(+), 14 deletions(-)
diff --git a/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js b/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js
index bd0dfc7383f..4015de904c5 100644
--- a/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js
+++ b/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js
@@ -19,7 +19,7 @@ import {RNTesterThemeContext} from '../../components/RNTesterTheme';
import RNTOption from '../../components/RNTOption';
import * as React from 'react';
import {useCallback, useContext, useState} from 'react';
-import {Modal, Platform, StyleSheet, Switch, Text, View} from 'react-native';
+import {Modal, Platform, StyleSheet, Switch, Text, TextInput, View} from 'react-native';
const animationTypes = ['slide', 'none', 'fade'] as const;
const presentationStyles = [
@@ -70,6 +70,9 @@ function ModalPresentation() {
ios: ['portrait'],
}),
transparent: false,
+ hideBorder: false, // Windows
+ hideTitleBar: false, // Windows
+ title: 'Modal Presentation', // Windows
visible: false,
});
const presentationStyle = props.presentationStyle;
@@ -190,6 +193,37 @@ function ModalPresentation() {
}
/>
+ {/* [Windows] - HideTitleBar is a Windows only prop. It is not supported on iOS or Android. */}
+
+ HideTitleBar
+
+ setProps(prev => ({...prev, hideTitleBar: enabled}))
+ }
+ />
+
+ {/* [Windows] - HideBorder is a Windows only prop. It is not supported on iOS or Android. */}
+
+ HideBorder
+
+ setProps(prev => ({...prev, hideBorder: enabled}))
+ }
+ />
+
+ {/* [Windows] - Title is a Windows only prop. It is not supported on iOS or Android. */}
+
+ Title
+
+ setProps(prev => ({...prev, title: text}))
+ }
+ />
+
{Platform.OS === 'ios' && presentationStyle !== 'overFullScreen' ? (
iOS Modal can only be transparent with 'overFullScreen' Presentation
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
index 04f26782c47..275796fd02f 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
@@ -7,6 +7,7 @@
#include "../../../codegen/react/components/rnwcore/ModalHostView.g.h"
#include
+#include
#include
#include
#include
@@ -112,6 +113,12 @@ struct ModalHostView : public winrt::implements::UpdateProps(view, newProps, oldProps);
}
@@ -258,6 +265,67 @@ struct ModalHostView : public winrt::implementshideTitleBar.value_or(false) && m_localProps->hideBorder.value_or(false);
+
+ auto titleBar = m_rnWindow.AppWindow().TitleBar();
+ titleBar.ResetToDefault();
+
+ overlappedPresenter.SetBorderAndTitleBar(!hideBorders, !m_localProps->hideTitleBar.value_or(false));
+
+ auto hwnd = GetWindowFromWindowId(m_rnWindow.AppWindow().Id());
+
+ if (hideBorders) {
+ overlappedPresenter.IsResizable(false);
+
+ const DWMNCRENDERINGPOLICY ncPolicy = DWMNCRP_DISABLED;
+ ::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncPolicy, sizeof(ncPolicy));
+
+ const DWM_WINDOW_CORNER_PREFERENCE cornerPref = DWMWCP_DEFAULT;
+ ::DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref));
+
+ const COLORREF zeroColor = 0;
+ ::DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &zeroColor, sizeof(zeroColor));
+ ::DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &zeroColor, sizeof(zeroColor));
+ ::DwmSetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, &zeroColor, sizeof(zeroColor));
+ }
+ else
+ {
+ overlappedPresenter.IsResizable(true);
+
+ const DWMNCRENDERINGPOLICY ncPolicy = DWMNCRP_USEWINDOWSTYLE;
+ ::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncPolicy, sizeof(ncPolicy));
+
+ const DWM_WINDOW_CORNER_PREFERENCE cornerPref = DWMWCP_DEFAULT;
+ ::DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref));
+
+ const COLORREF zeroColor = DWMWA_COLOR_DEFAULT ;
+ ::DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &zeroColor, sizeof(zeroColor));
+ ::DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &zeroColor, sizeof(zeroColor));
+ ::DwmSetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, &zeroColor, sizeof(zeroColor));
+
+ titleBar.IconShowOptions(
+ winrt::Microsoft::UI::Windowing::IconShowOptions::HideIconAndSystemMenu);
+ }
+ }
+
// creates a new modal window
void EnsureModalCreated(const winrt::Microsoft::ReactNative::ComponentView &view) {
if (m_popUp) {
@@ -282,22 +350,10 @@ struct ModalHostView : public winrt::implementstitle.has_value()) {
diff --git a/vnext/src-win/Libraries/Modal/Modal.windows.js b/vnext/src-win/Libraries/Modal/Modal.windows.js
index 200e0239044..31c1c1b87ac 100644
--- a/vnext/src-win/Libraries/Modal/Modal.windows.js
+++ b/vnext/src-win/Libraries/Modal/Modal.windows.js
@@ -352,6 +352,8 @@ class Modal extends React.Component {
onOrientationChange={this.props.onOrientationChange}
allowSwipeDismissal={this.props.allowSwipeDismissal}
testID={this.props.testID}
+ hideTitleBar={this.props.hideTitleBar} // [Windows]
+ hideBorder={this.props.hideBorder} // [Windows]
title={this.props.title}>
diff --git a/vnext/src-win/src/private/specs_DEPRECATED/components/RCTModalHostViewNativeComponent.js b/vnext/src-win/src/private/specs_DEPRECATED/components/RCTModalHostViewNativeComponent.js
index 4f1e3f8077a..b8fb6976a77 100644
--- a/vnext/src-win/src/private/specs_DEPRECATED/components/RCTModalHostViewNativeComponent.js
+++ b/vnext/src-win/src/private/specs_DEPRECATED/components/RCTModalHostViewNativeComponent.js
@@ -152,6 +152,10 @@ type RCTModalHostViewNativeProps = Readonly<{|
*/
// [Windows
title?: WithDefault,
+
+ hideTitleBar?: WithDefault,
+
+ hideBorder?: WithDefault,
// Windows]
|}>;
From 3afce4beff2d01a97ce581fc2c3c04aa430a7149 Mon Sep 17 00:00:00 2001
From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com>
Date: Sun, 10 May 2026 11:44:40 -0700
Subject: [PATCH 2/5] Change files
---
...ative-windows-2dd3b1fe-b27b-4762-9038-1d1e1d3a4ab0.json | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 change/react-native-windows-2dd3b1fe-b27b-4762-9038-1d1e1d3a4ab0.json
diff --git a/change/react-native-windows-2dd3b1fe-b27b-4762-9038-1d1e1d3a4ab0.json b/change/react-native-windows-2dd3b1fe-b27b-4762-9038-1d1e1d3a4ab0.json
new file mode 100644
index 00000000000..2b7577436b5
--- /dev/null
+++ b/change/react-native-windows-2dd3b1fe-b27b-4762-9038-1d1e1d3a4ab0.json
@@ -0,0 +1,7 @@
+{
+ "type": "prerelease",
+ "comment": "Add hideTitleBar and hideBorder to Modal",
+ "packageName": "react-native-windows",
+ "email": "30809111+acoates-ms@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
From 9dcadffba3e3c9d251b1285fc254be0d4bf80ef3 Mon Sep 17 00:00:00 2001
From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com>
Date: Sun, 10 May 2026 12:17:19 -0700
Subject: [PATCH 3/5] format
---
.../Modal/WindowsModalHostViewComponentView.cpp | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
index 275796fd02f..5770ab3422d 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
@@ -287,14 +287,13 @@ struct ModalHostView : public winrt::implementshideTitleBar.value_or(false));
auto hwnd = GetWindowFromWindowId(m_rnWindow.AppWindow().Id());
if (hideBorders) {
- overlappedPresenter.IsResizable(false);
-
const DWMNCRENDERINGPOLICY ncPolicy = DWMNCRP_DISABLED;
::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncPolicy, sizeof(ncPolicy));
@@ -305,24 +304,19 @@ struct ModalHostView : public winrt::implementstitle.has_value()) {
winrt::hstring titleValue = winrt::to_hstring(m_localProps->title.value());
From 036b72c22ff8705bbbf6b838fca5c451c336a03d Mon Sep 17 00:00:00 2001
From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com>
Date: Sun, 10 May 2026 12:55:23 -0700
Subject: [PATCH 4/5] lint fix (type fixes)
---
vnext/src-win/Libraries/Modal/Modal.d.ts | 4 ++++
vnext/src-win/Libraries/Modal/Modal.windows.js | 2 ++
2 files changed, 6 insertions(+)
diff --git a/vnext/src-win/Libraries/Modal/Modal.d.ts b/vnext/src-win/Libraries/Modal/Modal.d.ts
index 66dc9abe8a0..f345348bae9 100644
--- a/vnext/src-win/Libraries/Modal/Modal.d.ts
+++ b/vnext/src-win/Libraries/Modal/Modal.d.ts
@@ -117,6 +117,10 @@ export interface ModalWindowsProps {
/* title for the modal, shown in the title bar */
// [Windows
title?: string | undefined;
+
+ hideTitleBar?: boolean | undefined;
+
+ hideBorder?: boolean | undefined;
// Windows]
}
diff --git a/vnext/src-win/Libraries/Modal/Modal.windows.js b/vnext/src-win/Libraries/Modal/Modal.windows.js
index 31c1c1b87ac..d02116df78b 100644
--- a/vnext/src-win/Libraries/Modal/Modal.windows.js
+++ b/vnext/src-win/Libraries/Modal/Modal.windows.js
@@ -174,6 +174,8 @@ export type ModalPropsWindows = {
* [Windows] The `title` prop sets the title of the modal window.
*/
title?: ?string,
+ hideTitleBar?: ?boolean,
+ hideBorder?: ?boolean,
};
export type ModalProps = {
From dd3303e300ce5c00c9e7e632d5e3fc96e6f877e4 Mon Sep 17 00:00:00 2001
From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com>
Date: Sun, 10 May 2026 18:35:35 -0700
Subject: [PATCH 5/5] snapshot
---
.../__snapshots__/snapshotPages.test.js.snap | 113 ++++++++++++++++++
1 file changed, 113 insertions(+)
diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap b/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap
index 336fd341df5..b0695e2cae6 100644
--- a/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap
+++ b/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap
@@ -31656,6 +31656,119 @@ exports[`snapshotAllPages Modal 1`] = `
value={false}
/>
+
+
+ HideTitleBar
+
+
+
+
+
+ HideBorder
+
+
+
+
+
+ Title
+
+
+