{"id":50103150,"url":"https://github.com/MaxAst/expo-share-extension","last_synced_at":"2026-06-09T00:00:50.565Z","repository":{"id":209524198,"uuid":"716957382","full_name":"MaxAst/expo-share-extension","owner":"MaxAst","description":"Expo config plugin for creating iOS share extensions with a custom view.","archived":false,"fork":false,"pushed_at":"2026-04-12T06:53:07.000Z","size":2783,"stargazers_count":532,"open_issues_count":23,"forks_count":29,"subscribers_count":9,"default_branch":"main","last_synced_at":"2026-04-25T15:00:02.830Z","etag":null,"topics":["expo","ios","react-native","share-extension"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MaxAst.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["MaxAst"]}},"created_at":"2023-11-10T08:30:28.000Z","updated_at":"2026-04-24T20:25:55.000Z","dependencies_parsed_at":"2023-11-28T22:27:58.477Z","dependency_job_id":"580edb59-d511-4be9-851a-af3a1cdfdd40","html_url":"https://github.com/MaxAst/expo-share-extension","commit_stats":null,"previous_names":["maxast/expo-share-extension"],"tags_count":64,"template":false,"template_full_name":null,"purl":"pkg:github/MaxAst/expo-share-extension","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxAst%2Fexpo-share-extension","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxAst%2Fexpo-share-extension/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxAst%2Fexpo-share-extension/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxAst%2Fexpo-share-extension/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MaxAst","download_url":"https://codeload.github.com/MaxAst/expo-share-extension/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxAst%2Fexpo-share-extension/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34085321,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["expo","ios","react-native","share-extension"],"created_at":"2026-05-23T09:00:21.140Z","updated_at":"2026-06-09T00:00:50.558Z","avatar_url":"https://github.com/MaxAst.png","language":"TypeScript","funding_links":["https://github.com/sponsors/MaxAst"],"categories":["TypeScript"],"sub_categories":[],"readme":"# Expo Share Extension\n\n![npm](https://img.shields.io/npm/v/expo-share-extension.svg)\n![License](https://img.shields.io/npm/l/expo-share-extension.svg)\n![Downloads](https://img.shields.io/npm/dm/expo-share-extension.svg)\n![GitHub stars](https://img.shields.io/github/stars/MaxAst/expo-share-extension.svg)\n\n\u003e **Note**: The default `Text` and `TextInput` components by React Native do not work in the share extension due to a font scaling issue. You can fix this by setting `allowFontScaling={false}` or by importing the given components from `expo-share-extension`.\n\n## Overview\n\nCreate an [iOS share extension](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/Share.html) with a custom view (similar to e.g. Pinterest). Supports Apple Sign-In, [React Native Firebase](https://rnfirebase.io/) (including shared auth session via access groups), custom background, custom height, and custom fonts.\n\nhttps://github.com/MaxAst/expo-share-extension/assets/13224092/e5a6fb3d-6c85-4571-99c8-4efe0f862266\n\n## Compatibility\n\n| Expo       | `expo-share-extension` |\n| ---------- | ---------------------- |\n| **SDK 54** | 5.0.0+                 |\n| **SDK 53** | 4.0.0+                 |\n| **SDK 52** | 2.0.0+ and 3.0.0+      |\n| **SDK 51** | 1.5.3+                 |\n| **SDK 50** | 1.0.0+                 |\n\n## Quick Start\n\n### 1. Installation\n\n```sh\nnpx expo install expo-share-extension\n```\n\n### 2. Basic Configuration\n\n1. Update your `app.json` or `app.config.js`:\n\n```json\n\"expo\": {\n  ...\n  \"plugins\": [\"expo-share-extension\"],\n  ...\n}\n```\n\n2. Ensure your `package.json` has the correct `main` entry:\n\n```json\n{\n  ...\n  \"main\": \"index.js\",\n  ...\n}\n```\n\n3. Create the required entry points:\n\n`index.js` (main app):\n\n```ts\nimport { registerRootComponent } from \"expo\";\n\nimport App from \"./App\";\n\nregisterRootComponent(App);\n\n// or if you're using expo-router:\n// import \"expo-router/entry\";\n```\n\n`index.share.js` (share extension):\n\n```ts\nimport { AppRegistry } from \"react-native\";\n\n// could be any component you want to use as the root component of your share extension's bundle\nimport ShareExtension from \"./ShareExtension\";\n\n// IMPORTANT: the first argument to registerComponent, must be \"shareExtension\"\nAppRegistry.registerComponent(\"shareExtension\", () =\u003e ShareExtension);\n```\n\n4. Wrap your metro config with `withShareExtension` in metro.config.js (if you don't have one, run: `npx expo customize metro.config.js` first):\n\n```js\n// Learn more https://docs.expo.io/guides/customizing-metro\nconst { getDefaultConfig } = require(\"expo/metro-config\");\nconst { withShareExtension } = require(\"expo-share-extension/metro\");\n\nmodule.exports = withShareExtension(getDefaultConfig(__dirname), {\n  // [Web-only]: Enables CSS support in Metro.\n  isCSSEnabled: true,\n});\n```\n\n## Accessing Shared Data\n\nThe shared data is passed to the share extension's root component as an initial prop based on this type:\n\n```ts\nexport type InitialProps = {\n  files?: string[];\n  images?: string[];\n  videos?: string[];\n  text?: string;\n  url?: string;\n  preprocessingResults?: unknown;\n};\n```\n\nYou can import `InitialProps` from `expo-share-extension` to use it as a type for your root component's props.\n\n## Activation Rules\n\nThe config plugin supports almost all [NSExtensionActivationRules](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/AppExtensionKeys.html#//apple_ref/doc/uid/TP40014212-SW10). It currently supports.\n\n- `NSExtensionActivationSupportsText`, which is triggered e.g. when sharing a WhatsApp message's contents or when selecting a text on a webpage and sharing it via the iOS tooltip menu. The result is passed as the `text` field in the initial props\n- `NSExtensionActivationSupportsWebURLWithMaxCount: 1`, which is triggered when using the share button in Safari. The result is passed as the `url` field in the initial props\n- `NSExtensionActivationSupportsWebPageWithMaxCount: 1`, which is triggered when using the share button in Safari. The result is passed as the `preprocessingResults` field in the initial props. When using this rule, you will no longer receive `url` as part of initial props, unless you extract it in your preprocessing JavaScript file. You can learn more about this in the [Preprocessing JavaScript](#preprocessing-javascript) section.\n- `NSExtensionActivationSupportsImageWithMaxCount: 1`, which is triggered when using the share button on an image. The result is passed as part of the `images` array in the initial props.\n- `NSExtensionActivationSupportsMovieWithMaxCount: 1`, which is triggered when using the share button on a video. The result is passed as part of the `videos` array in the initial props.\n- `NSExtensionActivationSupportsFileWithMaxCount: 1`, which is triggered when using the share button on a file. The result is passed as part of the `files` array in the initial props.\n\nYou need to list the activation rules you want to use in your `app.json`/`app.config.(j|t)s` file like so:\n\n```json\n[\n  \"expo-share-extension\",\n  {\n    \"activationRules\": [\n      {\n        \"type\": \"file\",\n        \"max\": 3\n      },\n      {\n        \"type\": \"image\",\n        \"max\": 2\n      },\n      {\n        \"type\": \"video\",\n        \"max\": 1\n      },\n      {\n        \"type\": \"text\"\n      },\n      {\n        \"type\": \"url\",\n        \"max\": 1\n      }\n    ]\n  }\n]\n```\n\nIf no values for `max` are provided, the default value is `1`. The `type` field can be one of the following: `file`, `image`, `video`, `text`, `url`.\n\nIf you want to use the `image` and `video` types, you need to make sure to add this to your `app.json`:\n\n```jsonc\n{\n  // ...\n  \"ios\": {\n    // ...\n    \"privacyManifests\": {\n      \"NSPrivacyAccessedAPITypes\": [\n        {\n          \"NSPrivacyAccessedAPIType\": \"NSPrivacyAccessedAPICategoryFileTimestamp\",\n          \"NSPrivacyAccessedAPITypeReasons\": [\"C617.1\"],\n        },\n        // ...\n      ],\n    },\n  },\n}\n```\n\nIf you do not specify the `activationRules` option, `expo-share-extension` enables the `url` and `text` rules by default, for backwards compatibility.\n\nContributions to support the remaining [NSExtensionActivationRules](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/AppExtensionKeys.html#//apple_ref/doc/uid/TP40014212-SW10) (`NSExtensionActivationSupportsAttachmentsWithMaxCount` and `NSExtensionActivationSupportsAttachmentsWithMinCount`) are welcome!\n\n## Basic Usage\n\nNeed a way to close the share extension? Use the `close` method from `expo-share-extension`:\n\n```ts\nimport { close } from \"expo-share-extension\"\nimport { Button, Text, View } from \"react-native\";\n\n// if ShareExtension is your root component, url is available as an initial prop\nexport default function ShareExtension({ url }: { url: string }) {\n  return (\n    \u003cView style={{ flex: 1 }}\u003e\n      \u003cText\u003e{url}\u003c/Text\u003e\n      \u003cButton title=\"Close\" onPress={close} /\u003e\n    \u003c/View\u003e\n  );\n}\n```\n\nIf you want to open the host app from the share extension, use the `openHostApp` method from `expo-share-extension` with a valid path:\n\n```ts\nimport { openHostApp } from \"expo-share-extension\"\nimport { Button, Text, View } from \"react-native\";\n\n// if ShareExtension is your root component, url is available as an initial prop\nexport default function ShareExtension({ url }: { url: string }) {\n  const handleOpenHostApp = () =\u003e {\n    openHostApp(`create?url=${url}`)\n  }\n\n  return (\n    \u003cView style={{ flex: 1 }}\u003e\n      \u003cText\u003e{url}\u003c/Text\u003e\n      \u003cButton title=\"Open Host App\" onPress={handleOpenHostApp} /\u003e\n    \u003c/View\u003e\n  );\n}\n```\n\nWhen you share images and videos, `expo-share-extension` stores them in a `sharedData` directory in your app group's container.\nThese files are not automatically cleaned up, so you should delete them when you're done with them. You can use the `clearAppGroupContainer` method from `expo-share-extension` to delete them:\n\n```ts\nimport { clearAppGroupContainer } from \"expo-share-extension\"\nimport { Button, Text, View } from \"react-native\";\n\n// if ShareExtension is your root component, url is available as an initial prop\nexport default function ShareExtension({ url }: { url: string }) {\n  const handleCleanUp = async () =\u003e {\n    await clearAppGroupContainer()\n  }\n\n  return (\n    \u003cView style={{ flex: 1 }}\u003e\n      \u003cText\u003eI have finished processing all shared images and videos\u003c/Text\u003e\n      \u003cButton title=\"Clear App Group Container\" onPress={handleOpenHostApp} /\u003e\n    \u003c/View\u003e\n  );\n}\n```\n\n## Configuration Options\n\n### Exlude Expo Modules\n\nExclude unneeded expo modules to reduce the share extension's bundle size by adding the following to your `app.json`/`app.config.(j|t)s`:\n\n```json\n[\n  \"expo-share-extension\",\n    {\n      \"excludedPackages\": [\n        \"expo-dev-client\",\n        \"expo-splash-screen\",\n        \"expo-updates\",\n        \"expo-font\",\n      ],\n    },\n],\n```\n\n**Note**: The share extension should not include app-level runtime/dev modules such as `expo-updates`, `expo-dev-client`, `expo-dev-launcher`, and `expo-dev-menu` in its target. These are excluded by default. If you're using an older version, you should exclude them manually via `excludedPackages` in your `app.json`/`app.config.(j|t)s`.\n\n### React Native Firebase\n\nUsing [React Native Firebase](https://rnfirebase.io/)? Given that share extensions are separate iOS targets, they have their own bundle IDs, so we need to create a _dedicated_ GoogleService-Info.plist in the Firebase console, just for the share extension target. The bundle ID of your share extension is your existing bundle ID with `.ShareExtension` as the suffix, e.g. `com.example.app.ShareExtension`.\n\n```json\n[\n  \"expo-share-extension\",\n    {\n      \"googleServicesFile\": \"./path-to-your-separate/GoogleService-Info.plist\",\n    },\n],\n```\n\nYou can share a firebase auth session between your main app and the share extension by using the [`useUserAccessGroup` hook](https://rnfirebase.io/reference/auth#useUserAccessGroup). The value for `userAccessGroup` is your main app's bundle ID with the `group.` prefix, e.g. `group.com.example.app`. For a full example, check [this](examples/with-firebase/README.md).\n\n### Custom Background Color\n\nWant to customize the share extension's background color? Add the following to your `app.json`/`app.config.(j|t)s`:\n\n```json\n[\n  \"expo-share-extension\",\n    {\n      \"backgroundColor\": {\n        \"red\": 255,\n        \"green\": 255,\n        \"blue\": 255,\n        \"alpha\": 0.8 // if 0, the background will be transparent\n      },\n    },\n],\n```\n\n### Custom Height\n\nWant to customize the share extension's height? Do this in your `app.json`/`app.config.(j|t)s`:\n\n```json\n[\n  \"expo-share-extension\",\n    {\n      \"height\": 500\n    },\n],\n```\n\n### Custom Fonts\n\nThis plugin automatically adds custom fonts to the share extension target if they are [embedded in the native project](https://docs.expo.dev/develop/user-interface/fonts/#embed-font-in-a-native-project) via the `expo-font` config plugin.\n\nIt currently does not support custom fonts that are [loaded at runtime](https://docs.expo.dev/develop/user-interface/fonts/#load-font-at-runtime), due to an `NSURLSesssion` [error](https://stackoverflow.com/questions/26172783/upload-nsurlsesssion-becomes-invalidated-in-sharing-extension-in-ios8-with-error). To fix this, Expo would need to support defining a [`sharedContainerIdentifier`](https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1409450-sharedcontaineridentifier) for `NSURLSessionConfiguration` instances, where the value would be set to the main app's and share extension's app group identifier (e.g. `group.com.example.app`).\n\n### Preprocessing JavaScript\n\nAs explained in [Accessing a Webpage](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW12), we can use a JavaScript file to preprocess the webpage before the share extension is activated. This is useful if you want to extract the title and URL of the webpage, for example. To use this feature, add the following to your `app.json`/`app.config.(j|t)s`:\n\n```json\n[\n  \"expo-share-extension\",\n    {\n      \"preprocessingFile\": \"./preprocessing.js\"\n    },\n],\n```\n\nThe `preprocessingFile` option adds [`NSExtensionActivationSupportsWebPageWithMaxCount: 1`](https://developer.apple.com/documentation/bundleresources/information_property_list/nsextension/nsextensionattributes/nsextensionactivationrule/nsextensionactivationsupportswebpagewithmaxcount) as an `NSExtensionActivationRule`. Your preprocessing file must adhere to some rules:\n\n1. You must create a class with a `run` method, which receives an object with a `completionFunction` method as its argument. This `completionFunction` method must be invoked at the end of your `run` method. The argument you pass to it, is what you will receive as the `preprocessingResults` object as part of initial props.\n\n```javascript\nclass ShareExtensionPreprocessor {\n  run(args) {\n    args.completionFunction({\n      title: document.title,\n    });\n  }\n}\n```\n\n2. Your file must create an instance of a class using `var`, so that it is globally accessible.\n\n```javascript\nvar ExtensionPreprocessingJS = new ShareExtensionPreprocessor();\n```\n\nFor a full example, check [this](examples/with-preprocessing/README.md).\n\n**WARNING:** Using this option enables [`NSExtensionActivationSupportsWebPageWithMaxCount: 1`](https://developer.apple.com/documentation/bundleresources/information_property_list/nsextension/nsextensionattributes/nsextensionactivationrule/nsextensionactivationsupportswebpagewithmaxcount) and this is mutually exclusive with [`NSExtensionActivationSupportsWebURLWithMaxCount: 1`](https://developer.apple.com/documentation/bundleresources/information_property_list/nsextension/nsextensionattributes/nsextensionactivationrule/nsextensionactivationsupportsweburlwithmaxcount), which `expo-share-extension` enables by default. This means that once you set the `preprocessingFile` option, you will no longer receive `url` as part of initial props. However, you can still get the URL via `preprocessingResults` by using `window.location.href` in your preprocessing file:\n\n```javascript\nclass ShareExtensionPreprocessor {\n  run(args) {\n    args.completionFunction({\n      url: window.location.href,\n      title: document.title,\n    });\n  }\n}\n```\n\n## App Group\n\nBy default the App Group is set to the bundle identifier with the `group.` prefix, e.g. `group.com.example.app`.\n\nYou can override this by setting the `AppGroup` or `AppGroupIdentifier` key in your the `infoPlist` configuration of your Expo config, and it will be grabbed from there instead.\n\nExample:\n\n```json\n{\n  \"expo\": {\n    // ..rest of your app.json config..\n    \"ios\": {\n      \"infoPlist\": {\n        \"AppGroup\": \"group.com.example.app\", // First priority.\n        \"AppGroupIdentifier\": \"group.com.example.app\" // Second priority.\n      }\n    }\n  }\n```\n\n## Development\n\nIf you want to contribute to this project, you can use the example app to test your changes. Run the following commands to get started:\n\n1. Start the expo module build in watch mode: `npm run build`\n2. Start the config plugin build in watch mode: `npm run build plugin`\n3. `cd /example` and generate the iOS project: `npm run prebuild`\n4. Run the app from the /example folder: `npm run ios`\n\n### Troubleshooting\n\n#### Command PhaseScriptExecution failed with a nonzero exit code\n\nIf you encounter this error when building your app in XCode and you use yarn as a package manager, it is most likely caused by XCode using the wrong node binary. To fix this, navigate into your project's ios directory and replace the contents in the `.xcode.env.local` file with the contents of the `.xcode.env` file.\n\n#### Clear XCode Cache\n\n1. navigate to `~/Library/Developer/Xcode/DerivedData/`\n2. `rm -rf` folders that are prefixed with your project name\n\n#### Clear CocoaPods Cache\n\n1. `pod cache clean --all`\n2. `pod deintegrate`\n\n#### Attach Debugger to Share Extension Process:\n\n1. In XCode in the top menu, navigate to Debug \u003e Attach to Process.\n2. In the submenu, you should see a list of running processes. Find your share extension's name in this list. If you don't see it, you can try typing its name into the search box at the bottom.\n3. Once you've located your share extension's process, click on it to attach the debugger to that process.\n4. With the debugger attached, you can also set breakpoints within your share extension's code. If these breakpoints are hit, Xcode will pause execution and allow you to inspect variables and step through your code, just like you would with your main app.\n\n#### Check Device Logs\n\n1. Open the Console app from the Applications/Utilities folder\n2. Select your device from the Devices list\n3. Filter the log messages by process name matching your share extension target name\n\n#### Check Crash Logs\n\n1. On your Mac, open Finder.\n2. Select Go \u003e Go to Folder from the menu bar or press Shift + Cmd + G.\n3. Enter ~/Library/Logs/DiagnosticReports/ and click Go.\n4. Look for any recent crash logs related to your share extension. These logs should have a .crash or .ips extension.\n\n## Credits\n\nThis project would not be possible without existing work in the react native ecosystem. I'd like to give credit to the following projects and their authors:\n\n- https://github.com/Expensify/react-native-share-menu\n- https://github.com/andrewsardone/react-native-ios-share-extension\n- https://github.com/alinz/react-native-share-extension\n- https://github.com/ajith-ab/react-native-receive-sharing-intent\n- https://github.com/timedtext/expo-config-plugin-ios-share-extension\n- https://github.com/achorein/expo-share-intent-demo\n- https://github.com/andrewsardone/react-native-ios-share-extension\n- https://github.com/EvanBacon/pillar-valley/tree/master/targets/widgets\n- https://github.com/andrew-levy/react-native-safari-extension\n- https://github.com/bndkt/react-native-app-clip\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMaxAst%2Fexpo-share-extension","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMaxAst%2Fexpo-share-extension","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMaxAst%2Fexpo-share-extension/lists"}