iOS Share Extension UI Template for React Native
A share extension puts your app inside every other app. The catch: it is not your React Native app, it is a constrained native process.
TL;DR
An iOS share extension is the sheet that appears when a user shares into your app, and the build turns on one fact: the extension is a separate native process in a tight memory sandbox, short-lived and not your React Native runtime. So the sheet is a lightweight native (or tiny RN) capture UI that writes the shared item into an App Group container and defers the heavy lifting to the main app, never uploading or rendering a full screen in-sheet (which gets it killed mid-share). Use a scaffolding library like react-native-share-menu but expect native config: the extension target, App Group entitlement, and Info.plist activation rules. A free VP0 design supplies the capture sheet and inbox screens.
What is a share extension, and why is it harder than it looks?
The screen that appears when a user taps Share in any app and picks yours: a small sheet where they can save a link, image, or text into your app without leaving the one they are in. It is one of the highest-value integrations a mobile app can have (it puts your app inside every other app), and in React Native it is also one of the most awkward, because a share extension is not your React Native app.
That is the fact the whole build turns on. An app extension runs as a separate process from your main app, in a tightly memory-limited sandbox, and it is native iOS (Swift), not your JS bundle. So the question is never “how do I show my React Native share screen,” it is “how much can I do in a constrained native extension, and what do I hand off to the main app.”
What can the extension actually do under its constraints?
Less than a full screen, by design, and the constraints are the spec:
| Constraint | What it means | Design consequence |
|---|---|---|
| Separate process | The extension is not your RN runtime | The sheet UI is native or a tiny RN slice, not your app |
| Tight memory budget | Extensions are killed for using too much | Keep it lightweight; no heavy work in the sheet |
| Short-lived | Dismissed in seconds | Capture intent, defer the work |
| Sandboxed | No direct access to the main app’s data | Share data through an App Group |
The pattern that respects all four: the extension’s sheet is a small, fast capture UI (what is being shared, which destination, an optional note), it writes the shared item into a shared container, and the heavy lifting happens later in the main app, not in the extension. A share extension that tries to upload a video, call your API, and render your full UI in-sheet is the one that gets killed mid-share, the most common failure in the genre.
How do the pieces fit in a React Native project?
Through an App Group and a deliberate native boundary. The shared container is the key mechanism: both the extension and the main app are members of the same App Group, which gives them a shared file location and shared UserDefaults, and that is how the captured item crosses from the extension’s process to yours. The extension reads the incoming item from its NSExtensionContext, writes it (or a reference to it) into the App Group container, and signals the main app.
In React Native specifically, three honest notes. First, libraries like react-native-share-menu (around 2,737 weekly downloads) and expo-share-extension exist to scaffold this, and they are the right starting point rather than hand-rolling the native target, though you still touch native config (the extension target, the App Group entitlement, Info.plist activation rules). Second, the activation rules in Info.plist decide which content types your extension appears for (URLs, images, text), and getting these right is what makes your app show up for the right shares and not others. Third, the extension can render a minimal React Native UI in some setups, but the lighter it is the more reliable it is, so a near-native capture sheet beats a full RN screen here.
The architecture rhymes with every native-boundary feature: the JS side stays thin, the native side does the OS integration, the same Turbo-Module-shaped seam as any native module integration and the same thin-JS, heavy-native split as the IAP boilerplate, with the App Group standing in for the bridge.
What does the share sheet owe the user?
Speed and an honest handoff. The capture sheet should appear instantly (the user is mid-flow in another app), confirm what was captured in one glance, and dismiss fast, with the actual processing happening when the user next opens the main app or in a background task the main app runs. If the share needs an account the user is not signed into, the honest move is to capture the item anyway and prompt for sign-in in the main app, never to dead-end the share sheet with a login wall.
Two states complete it: a clear success confirmation in the sheet (“Saved to [App]”) so the user knows it worked before returning to where they were, and graceful handling of the shared types you do not support (appear only for what you can actually handle, via the activation rules, rather than accepting a share and silently dropping it). The screens around the feature, the capture sheet, the main-app inbox where shared items land, the processing states, come as free VP0 designs an agent builds the native boundary onto, so the work lands on the App Group plumbing rather than the layout. The deferred-processing model is the same capture-now-process-later discipline as the Instagram story share export, run from the receiving side. The same native-feature-plus-React-Native-bridge pattern drives Siri Shortcuts and App Intents in React Native.
Key takeaways: a React Native share extension
- The extension is a separate native process, not your RN app: memory-limited, short-lived, sandboxed.
- Capture intent, defer the work: a light sheet writes to a shared container; the main app does the heavy lifting later.
- The App Group is the bridge: shared container and UserDefaults carry the item from the extension’s process to yours.
- Use a scaffolding library, but expect native config: the extension target, App Group entitlement, and Info.plist activation rules are yours to set.
- Activation rules decide where you appear: claim only the content types you can actually handle, and confirm the capture before dismissing.
Frequently asked questions
How do I build an iOS share extension for a React Native app? Add a native share-extension target (a library like react-native-share-menu or expo-share-extension scaffolds it), keep the sheet a lightweight capture UI, and pass the shared item through an App Group container to your main app, which does the heavy processing later. A free VP0 design supplies the capture sheet and main-app inbox screens to build the native boundary onto.
Why can’t the share extension just run my React Native app? Because an app extension is a separate process in a tight memory sandbox, killed for using too much, and is native iOS rather than your JS runtime. A full RN screen doing real work in the sheet gets terminated mid-share, so the extension stays light and defers the work to the main app.
How does data get from the share extension to the main app? Through an App Group: both targets join the same group and share a file container and UserDefaults, so the extension writes the captured item there and the main app reads it on next launch or in a background task. The sandbox blocks any more direct access.
What does the Info.plist activation rule do in a share extension? It declares which content types (URLs, images, text) your extension appears for in the share sheet. Set it to claim only what you can actually handle, so your app shows up for the right shares and never accepts a type it would silently drop.
Should the share sheet do the upload or processing itself? No: capture the intent fast, confirm it, and dismiss, then process in the main app or a background task. The extension is memory-limited and short-lived, so uploading a video or calling your API in-sheet is the classic way to get killed mid-share.
Other questions VP0 users ask
How do I build an iOS share extension for a React Native app?
Add a native share-extension target (a library like react-native-share-menu or expo-share-extension scaffolds it), keep the sheet a lightweight capture UI, and pass the shared item through an App Group container to your main app, which does the heavy processing later. A free VP0 design supplies the capture sheet and main-app inbox screens to build the native boundary onto.
Why can't a share extension just run my React Native app?
Because an app extension is a separate process in a tight memory sandbox, killed for overuse, and is native iOS rather than your JS runtime. A full RN screen doing real work in the sheet gets terminated mid-share, so the extension stays light and defers the work to the main app.
How does data get from the share extension to the main app?
Through an App Group: both targets join the same group and share a file container and UserDefaults, so the extension writes the captured item there and the main app reads it on next launch or in a background task. The sandbox blocks any more direct access between the two processes.
What does the Info.plist activation rule do in a share extension?
It declares which content types (URLs, images, text) your extension appears for in the system share sheet. Set it to claim only what you can actually handle, so your app shows up for the right shares and never accepts a type it would silently drop.
Should the share sheet do the upload or processing itself?
No: capture the intent fast, confirm it, and dismiss, then process in the main app or a background task. The extension is memory-limited and short-lived, so uploading a video or calling your API in-sheet is the classic way to get killed mid-share.
Part of the React Native & Expo: Mobile Frontend Architecture hub. Browse all VP0 topics →
Keep reading
Build a Responsive iPhone-to-iPad Layout in React Native
A responsive tablet layout changes shape, it does not just scale up. Here is how to build an adaptive iPhone-to-iPad layout in React Native with breakpoints.
Build a High-Performance Candlestick Chart in React Native
A candlestick chart with thousands of candles and smooth pan-zoom needs Skia, not SVG. Here is how to build a high-performance candlestick chart in React Native.
Build an NS Flex Travel History Timeline in React Native
A travel history timeline lists past journeys by date. Here is how to build the NS Flex trip-history screen in React Native with fast scrolling and offline cache.
Build a Custom Screen Time Chart UI in React Native
A custom screen time chart has two parts: the usage data and the chart. Here is how to build the screen time chart UI in React Native, data limits and all.
Build a Free Sendbird-Style Chat UI in React Native
Sendbird's chat UI kit is tied to its backend. Here is how to build the same React Native chat screens, channel list, message bubbles, and composer, for free.
Build Infinite Scroll in React Native with TanStack Query
TanStack Query handles paging, a virtualized list handles rendering. Here is how to build infinite scroll in React Native with useInfiniteQuery and FlashList.