Skip to content

Sharing

@symbiote-native/sharing wraps expo-sharing — the platform share sheet that hands a local file to any other app that can accept it — so every SymbioteNative adapter can reach it, not just React. Like secure store and local auth, every export is a free function with no per-instance state, so there is no hook/composable/service to wrap — the React, Vue, and Angular entry points are plain re-exports of the same core.

OS platform Support
iOS ✅ live
Android ✅ live
Framework adapter Support
React ✅ live
Vue ✅ live
Angular ✅ live
Svelte ✅ live

expo-sharing has two halves, and this package ships one of them.

Half Upstream API Here
Outgoing — hand a local file to another app shareAsync, isAvailableAsync ✅ ported in full
Incoming — receive files other apps share into your app useIncomingShare, getSharedPayloads, getResolvedSharedPayloadsAsync, clearSharedPayloads ❌ not ported
Terminal window
npm install @symbiote-native/sharing

expo-sharing and expo-modules-core come along as regular dependencies, pinned to exact versions — never install either yourself, and never add the expo meta-package to your project (it bundles its own Metro/Babel pipeline, which conflicts with this project’s own).

The package’s native-link.json asks symbiote-expo-link for the Android Gradle dependency and module-map entry on every install. Nothing else is needed: opening the share sheet requires no iOS permission, so there is no usage-description string, and the SharingFileProvider plus the <queries> block Android needs on API 30+ ship inside expo-sharing’s own manifest and merge into your app automatically.

All four adapters re-export the exact same functions; there is no per-adapter hook/composable/service to reach for, since nothing here holds live state or a subscription.

import { useState } from 'react';
import { Button, Text, View } from '@symbiote-native/react';
import { isAvailableAsync, shareAsync } from '@symbiote-native/sharing/react';
export default function ShareReport({ fileUri }: { fileUri: string }) {
const [error, setError] = useState<string | null>(null);
async function onShare() {
if (!(await isAvailableAsync())) {
setError('Sharing is not available on this device');
return;
}
await shareAsync(fileUri, { mimeType: 'application/pdf', dialogTitle: 'Send the report' });
}
return (
<View>
<Button title="Share the report" onPress={onShare} />
{error === null ? null : <Text>{error}</Text>}
</View>
);
}

iOS presents the share sheet as a popover on iPad and needs somewhere to point it. Pass the rectangle of whatever the user tapped; without one the popover anchors to the bottom-center of the presenting view.

await shareAsync(fileUri, { anchor: { x: 40, y: 120, width: 1, height: 1 } });
Signature Description
isAvailableAsync(): Promise<boolean> Whether the share sheet can be opened on this device. Resolves true on Android and iOS — it reports on the presence of the native module, not on any device capability
shareAsync(url, options?): Promise<void> Opens the platform share sheet for the local file at url. Resolves once the sheet is dismissed, whether or not the user picked anything

url must be a non-empty string pointing at a file the app can read — a file:// URI, or a path from a file-system API. Anything else throws before the native call is made.

Field Type Description
mimeType string | undefined MIME type of the file, deciding which apps the chooser offers. Guessed from the file name when omitted. Android only
UTI string | undefined Uniform Type Identifier of the file. Accepted by the native options record but unread in expo-sharing@57.0.8; carried through for forward compatibility. iOS only
dialogTitle string | undefined Title of the share dialog. Android renders it as the chooser header; iOS assigns it to the activity controller, where most share sheets ignore it
anchor ISharingAnchor | undefined Rectangle the iPad popover points at. Ignored on iPhone and on Android. iOS only
Field Type Description
x number | undefined Horizontal offset in points, relative to the presenting view. Defaults to that view’s horizontal center
y number | undefined Vertical offset in points, relative to the presenting view. Defaults to that view’s bottom edge
width number | undefined Width of the anchor rectangle in points. Defaults to 0
height number | undefined Height of the anchor rectangle in points. Defaults to 0
  • url has to be local. A remote http(s) URL is not downloaded first — fetch it to a local file yourself, then share that file.
  • A resolved promise is not a delivery receipt. Neither platform reports which app the user picked, or whether they picked one at all: the promise resolves when the sheet closes. iOS resolves on every dismissal path, including “picked Print, then cancelled the print dialog”.
  • Android runs one share at a time. Calling shareAsync again while a chooser is still open rejects rather than queueing.
  • The share sheet can only be verified on a device or simulator. The headless test suite fakes the native module, so it proves argument marshalling and the error paths, not the sheet itself.

@symbiote-native/sharing ships zero React/Vue/Angular logic — expo-sharing’s own JS is hand-ported into this package’s core/, resolving the native module through expo-modules-core’s requireNativeModule rather than the expo meta-package this project never installs:

packages/sharing/src/
├── core/ # framework-agnostic: isAvailableAsync + shareAsync. native-module.ts resolves
│ # ExpoSharing via requireNativeModule
├── react/ # @symbiote-native/sharing/react — export * from '../core'
├── vue/ # @symbiote-native/sharing/vue — export * from '../core'
├── svelte/ # @symbiote-native/sharing/svelte — export * from '../core'
└── angular/ # @symbiote-native/sharing/angular — export * from '../core'

Same shape as secure store’s and local auth’s four adapter entries: single-file re-exports with no lifecycle code, since every export is a stateless free function — there is nothing for a hook, composable, rune, or service to subscribe to or clean up. The incoming-share half in the scope section above is exactly the part that would have needed one. The native code itself is never vendored or copied — expo-modules-autolinking resolves it straight out of node_modules (see the native setup guide).