Shipnative
ShipnativeShipnative
Sign in
GuideAugust 2026 · 9 min read

React Native Share: Share Sheet, Files, and Images

Sharing looks like a five-minute feature and usually is, right up to the first bug report: the link works on the founder’s iPhone and arrives blank on Android. Or the export button opens a sheet that shares a filename instead of a file. There are three separate tools here with three separate jobs, and picking the wrong one is most of the difficulty. This covers what each can do, the platform asymmetries that cause the common bugs, and the part most apps skip — making a shared link actually bring someone back into the app.

Three tools, three jobs

OptionAvailabilityHandlesDoes not
Share from react-nativeBuilt in, no installText and, on iOS, a linkFiles, images, specific apps
expo-sharingExpo Go supportedAny local file URI with a mime typePlain text with no file
react-native-shareNeeds a development buildApp-specific targets, base64, StoriesNothing much — it is the heavy option

The practical shape of most apps: the built-in API for “share this recipe”, expo-sharing for “export my data”, and react-native-share only if a designer has specifically asked for an Instagram Stories button.

Text and links: the built-in API

import { Share, Platform } from 'react-native';

async function shareRecipe(recipe: { title: string; id: string }) {
  const link = `https://yourapp.com/r/${recipe.id}`;

  try {
    const result = await Share.share(
      {
        // Android reads ONLY this field. Put the link in it.
        message: `${recipe.title}\n\n${link}`,
        // iOS uses this for a rich link preview; harmless elsewhere.
        ...(Platform.OS === 'ios' ? { url: link } : {}),
        title: recipe.title,          // Android chooser title / email subject
      },
      { dialogTitle: 'Share recipe' },
    );

    if (result.action === Share.sharedAction) {
      // result.activityType is iOS-only, and undefined for some targets.
      track('recipe_shared', { via: result.activityType ?? 'unknown' });
    }
  } catch (e) {
    // The user cancelling does NOT throw — this is a real failure.
    console.warn('share failed', e);
  }
}

The asymmetries worth memorising:

  • url is iOS-only. This single fact explains the majority of “sharing is broken on Android” reports. Duplicating the link into message costs you a slightly noisier iOS payload and fixes Android completely.
  • Cancelling is not an error. Dismissing the sheet resolves with dismissedAction on iOS rather than rejecting, so a catch that shows an error toast will never fire for a cancel — and if yours does fire, something genuinely failed.
  • title is not a headline. It sets the Android chooser title and becomes the subject line when the target is email. It is invisible in most messaging apps.

Files: expo-sharing

The built-in API cannot attach a file. For an export, a receipt, or a generated image, the flow is always the same: produce bytes, write them to a local URI, hand that URI to the sheet.

npx expo install expo-sharing expo-file-system
import * as Sharing from 'expo-sharing';
import * as FileSystem from 'expo-file-system';

async function exportCsv(rows: string[][]) {
  if (!(await Sharing.isAvailableAsync())) {
    // Sharing is unavailable on web and in some restricted contexts.
    return Alert.alert('Sharing is not available on this device');
  }

  const csv = rows.map((r) => r.join(',')).join('\n');

  // The filename is what the recipient sees. "export.csv" is a wasted opportunity.
  const uri = FileSystem.cacheDirectory + `expenses-${new Date().toISOString().slice(0, 10)}.csv`;
  await FileSystem.writeAsStringAsync(uri, csv, { encoding: FileSystem.EncodingType.UTF8 });

  await Sharing.shareAsync(uri, {
    mimeType: 'text/csv',            // Android target filtering
    UTI: 'public.comma-separated-values-text',  // iOS target filtering
    dialogTitle: 'Export expenses',
  });
}

Use cacheDirectory, not documentDirectory, for anything transient. The OS reclaims the cache under storage pressure, whereas the document directory is backed up and counted against the user’s storage forever — an export feature that quietly accumulates every CSV a user ever generated is a real bug that nobody notices for months. Set both mimeType and UTI: they are how each platform decides which apps appear in the sheet, and omitting them is why a PDF sometimes offers only “Save to Files”.

Sharing an image the app rendered

Share cards — a workout summary, a streak, a quote — are the highest-leverage share in most consumer apps, because an image travels further than a link. The recipe is a view capture plus the file flow above:

import { captureRef } from 'react-native-view-shot';

const cardRef = useRef<View>(null);

async function shareCard() {
  const uri = await captureRef(cardRef, { format: 'png', quality: 1, result: 'tmpfile' });
  await Sharing.shareAsync(uri, { mimeType: 'image/png', UTI: 'public.png' });
}

// Render the card at share dimensions, off-screen if it is not part of the layout:
<View ref={cardRef} collapsable={false} style={{ width: 1080, height: 1080, transform: [{ scale: 0.3 }] }}>
  <ShareCard streak={streak} />
</View>

collapsable={false} is required on Android: the platform optimises away views it thinks have no visual effect, and a captured view that got collapsed returns blank. And put the app name or handle inside the image itself. The caption is stripped the moment someone re-shares the screenshot, and the picture is the only part that survives.

The half most apps skip: the link going somewhere

A share is only worth building if the recipient ends up somewhere useful. Three things have to line up, and they are all outside the share call itself:

  • The URL must be a real web page. A custom scheme like yourapp://recipe/42 renders as dead text for anyone who does not have the app. Share an https link and let universal links route it into the app when it is installed.
  • That page needs Open Graph tags. Messaging apps fetch the URL server-side to build the preview. No title, description, or image means your share arrives as a bare grey link, which is a measurable difference in whether anyone taps it.
  • It should survive an install. A recipient without the app taps the link, sees the web page, installs, and lands on the home screen having lost the context entirely. Handling that properly means deferred deep linking, which is a real piece of work — worth doing only once sharing is measurably driving installs.

One more thing genuinely outside all three libraries: a share extension, the entry that makes your app appear in otherapps’ share sheets. That is native iOS and Android target configuration, not a JavaScript API, and no amount of react-native-share provides it.

Skip the boilerplate

Share sheet, file export, a card that renders at the right size — the same afternoon in every app. Describe yours at shipnative.dev and it generates a real React Native app with these patterns already in place, running on your phone in minutes, with the full Expo project available to export and own.

Frequently Asked Questions

Why does my shared link disappear on Android?

Because the url field of the built-in Share API is iOS-only. On Android only message is used, so a call passing the link in url shares an empty or text-only payload. The portable fix is to append the link to the message string and keep url as an iOS extra, where it produces a proper link preview.

What is the difference between the built-in Share API and expo-sharing?

Different jobs. React Native Share shares text and, on iOS, a URL — it cannot share a file from disk. expo-sharing does exactly one thing: open the native sheet for a local file URI, so it is what you use for a generated PDF, an exported CSV, or a rendered image. Many apps end up using both.

Do I need react-native-share?

Only for things the other two cannot do: targeting a specific app such as Instagram Stories or WhatsApp, sharing base64 data without writing a file first, or reading back which activity the user chose on Android. It is a native module, so it needs a development build. If you are sharing text, a link, or a file, the built-in API and expo-sharing cover it with no extra dependency.

Can I tell what the user shared to?

On iOS, yes — the built-in Share.share resolves with action set to sharedAction and an activityType identifying the target. On Android the result only tells you the sheet was dismissed, with no activity attribution, so any analytics you build on this will be iOS-only by nature. Count share sheet opens rather than destinations if you want a metric that is comparable across platforms.

How do I share an image the app generated?

Write it to a file first, then share the file URI. Capture the view with react-native-view-shot, save it into the cache directory with expo-file-system, and pass that URI to expo-sharing with a mimeType of image/png. Sharing a remote https URL as an image does not work — the sheet shares the link, not the picture.

Does sharing work in the iOS simulator?

Partly, and misleadingly. The sheet opens, but most targets are missing because the simulator has no Messages account, no WhatsApp, and no real Photos library. Sharing is one of the features worth testing on a physical device before you trust it — a payload that looks fine in the simulator can arrive as a bare filename in a real message thread.

→

Expo Deep Linking Setup

What has to be configured for a shared link to open the app.

Read guide →
→

First 1,000 Users Without Ads

Where sharing fits in distribution, and where it does not.

Read guide →

Ship a real React Native app today

Describe, preview, and export Expo code — free to start.

Build with ShipNative →
ShipNative logoShipnative

Build mobile apps with AI. Describe, preview, and ship to iOS & Android in minutes.

Features

Text to App AIApp Generator from ScreenshotPRD to Mobile App

Tools

All free toolsApp Cost CalculatorApp Name GeneratorApp Store Keyword ToolReact Native Components

Blog

All blog postsHow to Build an App Without CodingBest AI Tools for Real Mobile AppsExpo EAS App Store ChecklistLovable, Cursor & v0 for MobileBest AI App Builders in 2026React Native AI App Builder Guide

Legal

FAQTerms of ServicePrivacy Policy

© 2026 ShipNative. All rights reserved.