npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@facilitronworks/react-native-windows-modal

v0.1.0-pre.1

Published

A drop-in replacement for react-native's <Modal> that actually works on react-native-windows new architecture, where the native WindowsModalHostView renders transparent flex content as an empty, unclosable shell. Delegates to the real <Modal> on every oth

Readme

@facilitronworks/react-native-windows-modal

A drop-in replacement for react-native's <Modal> that works on React Native Windows new architecture (Fabric / Composition), where the core <Modal> does not.

JS/TSX only — no native code, no autolinking, no rebuild.

Status: pre-release (0.1.0-pre.1). Extracted from a shipping production Windows app (RNW 0.83.2) where it replaced every broken modal in the app.

What is verified here: see Verification status.

Why this exists

On RNW new-arch, <Modal> is backed by the native WindowsModalHostView (Microsoft.ReactNative/Fabric/Composition/Modal). That component does not render an in-app overlay the way iOS and Android do. It spins up a separate top-level OS windowDesktopPopupSiteBridge + OverlappedPresenter.IsModal(true) — with its own native title bar, border and close (X) button, and sizes that window from the child's layout frame (ContentSizePolicy::NoneAdjustWindowSize).

Two consequences break real-world modals:

1. transparent + flex content renders as an empty, unclosable shell. The overwhelmingly common modal pattern is a transparent host with a full-bleed overlay child:

<Modal transparent visible={open} onRequestClose={close}>
  <View style={{ flex: 1 }}>…</View>
</Modal>

Inside a content-sized popup there is no bounded size for flex: 1 to fill, so the frame measures {0, 0}, AdjustWindowSize bails, and you get an empty window. Worse, it is stuck: the in-content close button never mounts because the content never laid out, and the native title-bar X only emits onRequestClose — it does not close the window itself. If your handler is on the content that never rendered, there is no way out of the modal.

2. Non-transparent full-screen content becomes a second desktop window. Complete with title bar and chrome — nothing like the in-app sheet the same code produces on iOS and Android.

The fix

react-native-paper's <Modal> works on RNW precisely because it renders an in-tree <Portal> overlay in the same window/island — no native modal host is involved. This package mirrors that approach behind the standard <Modal> API:

  • On Windows: renders children into an absolutely-positioned, root-level <Portal> overlay. StyleSheet.absoluteFill gives the overlay a definite size, which is exactly what the content-sized native popup could not provide — so flex: 1 children lay out correctly.
  • On every other platform: delegates to the real react-native <Modal>, props spread through unchanged. iOS / Android / web behaviour is byte-for-byte identical.

That means you can swap the import globally and only change Windows behaviour.

Installation

npm install @facilitronworks/react-native-windows-modal
# or: yarn add @facilitronworks/react-native-windows-modal

Peer dependencies: react, react-native, and react-native-paper >= 5 — Paper's Portal is the overlay host, and Paper's useTheme() supplies the background colour for non-transparent modals.

You must have a <PaperProvider> (or a bare <Portal.Host>) mounted at your app root. That is what the Portal renders into. Without it the modal renders nothing on Windows.

// app root
<PaperProvider>
  <App />
</PaperProvider>

Usage

import WindowsModal from '@facilitronworks/react-native-windows-modal';

<WindowsModal transparent visible={open} onRequestClose={close}>
  <View style={{ flex: 1, justifyContent: 'center' }}>
    …
  </View>
</WindowsModal>

The simplest adoption is a project-wide import swap:

- import { Modal } from 'react-native';
+ import WindowsModal as Modal from '@facilitronworks/react-native-windows-modal';

Both a named export (WindowsModal) and a default export are provided.

Supported props, and what is ignored on Windows

Every ModalProps is forwarded verbatim on non-Windows platforms. On Windows, only the props that are meaningful for an in-tree overlay are honoured:

| Prop | Windows behaviour | | --- | --- | | visible | Honoured — false renders null. | | transparent | Honoured — when false, the overlay fills with backdropColor ?? theme.colors.background. | | backdropColor | Honoured (only when not transparent). | | onRequestClose | Honoured — also required for the tap-outside-to-close backdrop to be rendered at all. | | onShow | Called manually when visible flips true, to preserve the RN contract. | | unmask | Honoured — when true, forwards fsClass="fs-unmask" onto the overlay to un-mask it in FullStory session replay. Defaults to false (masked). See Session replay / FullStory. | | animationType | Ignored. No animation — the overlay appears instantly. | | onDismiss, statusBarTranslucent, hardwareAccelerated, supportedOrientations, presentationStyle, navigationBarTranslucent | Ignored (mobile/native-window concepts). |

Session replay / FullStory

FullStory (React Native) masks all content by default and reveals an element only when it carries the fs-unmask marker. On Windows this component renders its overlay with a plain react-native View, so — unlike the app-internal View wrapper it was extracted from, which hardcoded fsClass="fs-unmask" — the modal is masked in session replay.

To un-mask it, pass unmask:

<WindowsModal transparent visible={open} onRequestClose={close} unmask>
  …
</WindowsModal>

This forwards fsClass="fs-unmask" onto the overlay View. It defaults to false (masked) — the safe FullStory default — so opt in only where the modal content is safe to show in replay. Windows-only (the overlay is Windows-only); it is inert on other platforms.

Traps you will hit

  • No <Portal.Host> means no modal. The single most likely failure. If your app root has no PaperProvider, the Windows path silently renders nothing.
  • This is not a real modal — it does not trap focus. It is an overlay in the same window. Keyboard focus and screen-reader traversal can still reach the app tree underneath. Do not rely on it for anything security- or correctness-critical.
  • Escape does not close it. The native title-bar X is gone (that is the point), but nothing replaces it — wire your own key handling if you need it.
  • The backdrop only exists when you pass onRequestClose. Without that prop there is no Pressable scrim, so tapping outside does nothing. This is deliberate — a scrim that cannot close anything is a trap.
  • animationType silently does nothing on Windows. Modals appear and disappear instantly; a design that depends on a slide-in will look abrupt.
  • Sibling z-order. The overlay sets zIndex/elevation 9999, but two of these open simultaneously stack in Portal order, not by any explicit priority.
  • transparent behaves differently than you might read it. It controls whether this component paints a background fill, not whether the OS window is transparent (there is no OS window).

Verification status

Verified, on this exact packaged source:

  • Typechecks clean under TypeScript 5.9 in strict mode, resolved against real react, react-native, react-native-paper and @types/react installations (RN 0.83.2, Paper 5.14.5).
  • npm pack produces a tarball containing the actual TSX source.

NOT verified — do not assume any of these:

  • Runtime behaviour of this package. The logic is production-proven in the app it was extracted from, but the code as packaged here has not been rendered — not on Windows, not on any platform.

  • Two extraction changes are new and unexercised. The original used app-local modules; both were generalised for this package:

    1. the overlay container was an app-internal View wrapper (adding only a FullStory fsClass prop) → now a plain react-native View that re-applies fsClass="fs-unmask" only when the unmask prop is set;
    2. the background colour came from an app-local theme context → now useTheme() from react-native-paper.

    These compile and typecheck; they have not been observed on screen. The Paper useTheme() swap in particular assumes your Paper theme's colors.background is the colour you want behind a non-transparent modal.

  • No example app, no automated tests, no CI.

  • Behaviour with nested modals, and interaction with other Portal users, is untested.

License

MIT