Skip to content

arunabhverma/expo-paste-input

Repository files navigation

expo-paste-input

expo-paste-input is a lightweight wrapper around React Native TextInput that lets users paste images, stickers, and GIFs directly from the system clipboard on iOS and Android.

It works at the native level to intercept paste events before React Native handles them, giving you access to pasted media as local file URIs while keeping full control over your own TextInput component. On iOS, it also uses native text-input hooks to capture keyboard stickers that are not always exposed like normal clipboard image data.

See the original demo on Twitter

iOS Android

Features

  • Paste text, images, stickers, and multiple GIFs
  • Works on iOS and Android
  • True wrapper around TextInput (bring your own input)
  • No custom UI, no opinionated styles
  • Returns local file URIs for pasted media
  • Safe to import on Web (no crash, no-op)
  • Supports Expo SwiftUI TextField as well.

Installation

Quick install

npx expo install expo-paste-input

or

yarn add expo-paste-input

Rebuild the app (required)

This library uses native code, so you must rebuild.

npx expo run:ios
npx expo run:android

(Expo Go will not work)


Usage

Wrap your TextInput with TextInputWrapper:

import { TextInputWrapper } from "expo-paste-input";
import { TextInput } from "react-native";

export default function MyInput() {
  return (
    <TextInputWrapper
      onPaste={(payload) => {
        console.log(payload);
      }}
    >
      <TextInput placeholder="Paste here..." />
    </TextInputWrapper>
  );
}

Props

Prop Type Description
children React.ReactElement The TextInput (or any custom input) you want to wrap.
onPaste (payload: PasteEventPayload) => void Callback fired when a paste event is detected. Receives pasted text or image URIs.

Types

type PasteEventPayload =
  | { type: "text"; value: string }
  | { type: "images"; uris: string[] }
  | { type: "unsupported" };
  • text → pasted text
  • images → local file URIs (file://...)
  • unsupported → anything else

Why a wrapper?

This library does not reimplement TextInput.

Instead:

<TextInputWrapper>
  <TextInput />
</TextInputWrapper>

This means:

  • you keep full control of your input
  • works with any custom TextInput
  • no prop mirroring
  • future-proof with RN updates

Platform behavior

iOS

  • Intercepts native paste(_:)
  • Extracts images from UIPasteboard
  • Saves to temp files
  • Preserves GIFs and stickers
  • Adds native sticker handling for iOS keyboard media using text-input hooks (including adaptive image glyph insertion), because stickers are not always exposed like normal clipboard image data

Android

  • Uses OnReceiveContentListener + ActionMode
  • Prevents Android "Can't paste images" toast
  • Saves pasted media to cache
  • Stickers and images are handled through regular clipboard/content APIs

Notes

  • Image URIs are temporary files, move them if you need persistence.
  • Text paste events fire after the text is inserted.
  • Image paste events prevent default paste (since TextInput can't render images).
  • Web is currently a no-op implementation.

Inspiration

Inspired by work from:


License

MIT