npm.io
0.1.0-pre.0 • Published 2d ago

@facilitronworks/react-native-windows-dragdrop

Licence
MIT
Version
0.1.0-pre.0
Deps
0
Size
39 kB
Vulns
0
Weekly
0

@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-dragdrop
npx react-native autolink-windows
npx react-native run-windows
Required: 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 init

2. 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 IDropTarget is registered on the top-level HWND, 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.
  • OleInitialize ordering is load-bearing. Call it before the WinRT apartment init, on the UI thread. Get this wrong and RegisterDragDrop fails; 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 the WinMain wiring is missing or RegisterDragDrop failed. That is the point of it.
  • One attachment per process. AttachToWindow no-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, .idl files fall through to classic midl.exe instead of midlrt, and every namespace declaration fails with MIDL2025.

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, producing RNWDragDrop.dll, RNWDragDrop.winmd and RNWDragDrop.lib.
  • autolink-windows resolves the package and emits both the include and the ReactPackageProvider registration.

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.AttachToWindow runtimeclass 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 RNWDragDrop during extraction. Compiled, not observed firing.
  • x64, Win32, and Release configurations. Only ARM64 Debug was built.
  • The src/index.ts JS 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_FILEDESCRIPTOR items such as attachments dragged straight out of Outlook) are not handled — only CF_HDROP. Those drags will show "restricted".

License

MIT

Keywords