A native Expo module for cross-platform paste event handling in React Native TextInput components.
| iOS | Android |
|---|---|
Final.71.mp4 |
Final.72.Video.mp4 |
This is not a published npm package. This is the exact production-ready module used inside a real app. If you want to use it, copy the modules/text-input-wrapper folder into your Expo project and run it.
- Intercepts paste events on TextInput components
- Handles pasted text and images consistently across iOS and Android
- Supports pasting multiple images, including GIFs
- Returns file URIs for pasted images that you can use directly
type PasteEventPayload =
| { type: "text"; value: string }
| { type: "images"; uris: string[] }
| { type: "unsupported" };The module wraps a TextInput and intercepts paste events at the native level before they reach the default handler.
On iOS, the module uses method swizzling to intercept paste(_:) on the underlying UITextField/UITextView. When a paste is detected:
- For images: The paste is intercepted, images are extracted from
UIPasteboard, saved to temp files, and URIs are sent to JavaScript. GIFs are preserved as-is. - For text: The original paste proceeds normally, and the pasted text is forwarded to JavaScript.
The wrapper view is transparent and passes through all touch events to children.
On Android, the module uses OnReceiveContentListener (API 31+) and a custom ActionMode.Callback to intercept paste events from both keyboard and context menu:
- For images: The paste is consumed before Android shows the "Can't add images" toast. Images are decoded, saved to cache, and URIs are sent to JavaScript.
- For text: The original paste proceeds normally, and the pasted text is forwarded to JavaScript.
The wrapper view is non-interactive and delegates all touch events to children.
Copy the modules/text-input-wrapper folder into your Expo project's modules directory.
# If using Expo prebuild
npx expo prebuild
# Or if you already have native projects
cd ios && pod installWrap your TextInput with TextInputWrapper and handle the onPaste callback:
import {
TextInputWrapper,
PasteEventPayload,
} from "@/modules/text-input-wrapper";
import { TextInput } from "react-native";
function MyInput() {
const handlePaste = (payload: PasteEventPayload) => {
if (payload.type === "images") {
// payload.uris contains file:// URIs for each pasted image
console.log("Pasted images:", payload.uris);
} else if (payload.type === "text") {
// payload.value contains the pasted text
console.log("Pasted text:", payload.value);
}
};
return (
<TextInputWrapper onPaste={handlePaste}>
<TextInput placeholder="Paste here..." />
</TextInputWrapper>
);
}- This is intentionally not packaged as a library
- It's meant to be copied, modified, and extended for your specific needs
- The image URIs point to temporary files — move or copy them if you need persistence
- Text paste events fire after the text is inserted into the input
- Image paste events prevent the default paste (since TextInput can't display images)
If you want to build a library on top of this, feel free. Please credit Arunabh Verma as inspiration.
This project exists because of inspiration from:
- Fernando Rojo — Inspired by his blog post how paste input is implemented in the v0 app
- Blog + context: how native paste handling works in v0
- 𝕏: How we built the v0 iOS app
- v0 — The real-world product discussed in Fernando Rojo’s writing
Their work on pushing React Native closer to native platform conventions was the catalyst for building this.
Built by Arunabh Verma
Demo: X post ↗