@facilitronworks/react-native-windows-dragdrop
v0.1.0-pre.0
Published
Native file drag-and-drop for React Native Windows (new architecture). Registers a classic OLE IDropTarget on the app's main HWND and emits dropped file paths to JS — the drop-target support RNW has none of.
Downloads
136
Maintainers
Readme
@facilitronworks/react-native-windows-dragdrop
Native file drag-and-drop for React Native Windows on the new architecture (Fabric / Composition). Drag files from Explorer onto your app window and get their paths in JS.
Status: pre-release (
0.1.0-pre.0). The C++ implementation is extracted from a shipping production Windows app (RNW 0.83.2, Windows-on-ARM and x64) where it backs real file-attachment dropzones.What is verified here: see Verification status. Read it before depending on this.
Why this exists
React Native Windows has no drop-target support whatsoever. There is no onDrop, no DragDrop module, no way to opt a view in. Drag a file over an RNW window and you get the "restricted" cursor; let go and nothing happens.
This is easy to miss until late, because drag-and-drop is free on web. The same React codebase running under react-native-web gets HTML5 drag events from the DOM, so a dropzone that works perfectly in the browser build is inert in the Windows build — and desktop users, unlike phone users, absolutely expect to drag a file onto a window.
This package closes that gap the only way Win32 offers: a classic OLE IDropTarget registered on the app's top-level HWND, bridged to JS as a DeviceEventEmitter event.
Installation
npm install @facilitronworks/react-native-windows-dragdrop
# or: yarn add @facilitronworks/react-native-windows-dragdropnpx react-native autolink-windows
npx react-native run-windowsRequired: two lines of WinMain wiring
Unlike most native modules, this one cannot work from autolinking alone. A drop target must be registered against a real top-level window handle, and only your WinMain has one.
In your app's WinMain (typically windows/<App>/<App>.cpp):
1. Initialize OLE — before the WinRT apartment init. RegisterDragDrop requires an OLE-initialized STA thread. OleInitialize performs a compatible apartment initialization, so it must come first:
::OleInitialize(nullptr); // BEFORE winrt::init_apartment / the RNW app init2. Attach the drop target once the AppWindow exists:
#include <winrt/RNWDragDrop.h>
// ... after the AppWindow has been created:
winrt::RNWDragDrop::DragDrop::AttachToWindow(
reinterpret_cast<uint64_t>(
winrt::Microsoft::UI::GetWindowFromWindowId(appWindow.Id())));GetWindowFromWindowId (Win32 interop) is the reliable way to turn an AppWindow's WindowId into an HWND.
Why a runtimeclass and not just a C++ function? Autolinking hands your app the generated projection header <winrt/RNWDragDrop.h>, but not this project's raw C++ headers, and the library links as its own DLL whose .def exports only the WinRT activation entry points. The plain function RNWDragDrop::AttachToWindow(HWND) therefore is not reachable across that boundary; RNWDragDrop.DragDrop is its projected form.
If you skip this wiring, nothing breaks — isAvailable() simply returns false forever and no drops are delivered.
Usage
import {
isAvailable, addDropListener,
} from '@facilitronworks/react-native-windows-dragdrop';
useEffect(() => {
const unsubscribe = addDropListener(({ files }) => {
// files: absolute Win32 paths, e.g. ['C:\\Users\\me\\photo.jpg']
console.log(files);
});
return unsubscribe;
}, []);
// Gate the affordance on the real capability:
<Text>{isAvailable() ? 'Drag files here' : 'Click to upload'}</Text>addDropListener is a no-op returning a no-op on non-Windows platforms, so you do not need to branch on Platform.OS at the call site.
Traps you will hit
- Drops are WINDOW-level, not element-level. The
IDropTargetis registered on the top-levelHWND, so a drop anywhere on the window fires every listener. If you have more than one dropzone, track which is hovered in JS and ignore the event in the others. This module cannot hit-test for you — RNW's view tree is not addressable from the OLE callback. - The drag cursor is window-wide too. Any file drag over the window shows the COPY cursor, including over areas that are not dropzones. Non-file drags correctly show "restricted".
- You get paths, not contents. Reading the files is your problem — pair this with an RNW filesystem module. The paths are absolute Win32 paths with backslashes, not
file://URIs; convert if your upload path expects one. OleInitializeordering is load-bearing. Call it before the WinRT apartment init, on the UI thread. Get this wrong andRegisterDragDropfails; the module degrades silently to unavailable (by design — drag-drop should never be load-bearing) so you will see no error, just no drops.isAvailable()is not a platform check. It is false on Windows too, whenever theWinMainwiring is missing orRegisterDragDropfailed. That is the point of it.- One attachment per process.
AttachToWindowno-ops after the first successful call; you cannot move the target to a different window. - Elevation mismatch blocks drops silently. Windows UIPI refuses drags from a normal-integrity Explorer to an elevated (admin) app. If your app runs elevated, drops from Explorer will not arrive and nothing will report why.
- Building from source requires a NuGet restore first (
msbuild -restore). Without it the CppWinRT targets never load,.idlfiles fall through to classicmidl.exeinstead ofmidlrt, and everynamespacedeclaration fails withMIDL2025.
Verification status
Verified, on this exact packaged source:
- The packaged form compiles. Autolinked via a
portal:dependency into a consuming RNW 0.83.2 new-arch app and built clean for ARM64 Debug, producingRNWDragDrop.dll,RNWDragDrop.winmdandRNWDragDrop.lib. autolink-windowsresolves the package and emits both the include and theReactPackageProviderregistration.
NOT verified — do not assume any of these:
- Runtime behaviour of the DLL built from this project. The source is production-proven inside its original app, but the binary produced by this repo has not been exercised: no actual file has been dropped on an app consuming this package. In particular the
DragDrop.AttachToWindowruntimeclass boundary is new to this package — the original app called the plain C++ function directly, because the module was compiled into the app rather than into a separate DLL. That projection has been compiled but never called. - The event name changed from the app's internal name to
RNWDragDropduring extraction. Compiled, not observed firing. - x64, Win32, and Release configurations. Only ARM64 Debug was built.
- The
src/index.tsJS layer is new to this package. It typechecks; it has not been run. - No example app, no automated tests, no CI.
- Drops of non-file payloads (text, bitmaps, virtual/
CFSTR_FILEDESCRIPTORitems such as attachments dragged straight out of Outlook) are not handled — onlyCF_HDROP. Those drags will show "restricted".
License
MIT
