xref: /expo/docs/pages/bare/using-expo-client.mdx (revision 6eefacaa)
1---
2title: Custom native code in Expo Go
3description: Learn how to detect and disable unsupported native features in Expo Go.
4---
5
6import { Terminal } from '~/ui/components/Snippet';
7import { BoxLink } from '~/ui/components/BoxLink';
8import { CODE } from '~/ui/components/Text';
9import { SnackInline } from '~/ui/components/Snippet';
10
11The [Expo Go][expo-go] app can be used to experiment with most React Native apps regardless of how they are bootstrapped.
12
13The main drawback of iterating with Expo Go is that it is impossible to send custom native code over-the-air to the Expo Go app. This means you will need to do one of the following:
14
151. Create a [development build](/develop/development-builds/introduction/) to use custom native code, and distribute it with [internal distribution](/build/internal-distribution) or TestFlight.
162. Conditionally disable unsupported native features in Expo Go.
17
18This guide will demonstrate achieving the second option by compatible library versions, detecting whether the code is running in an Expo Go app at run time, native module detection, and so on.
19
20## Usage
21
22Inside any React Native app, start a [development server with Expo CLI](/more/expo-cli/#develop):
23
24<Terminal cmd={['$ npx expo start']} />
25
26<BoxLink
27  title={
28    <>
29      Don't have <CODE>npx expo start</CODE>?
30    </>
31  }
32  href="/bare/installing-expo-modules"
33  description={
34    <>
35      Install and configure the <CODE>expo</CODE> package in your project.
36    </>
37  }
38/>
39
40Then, launch your project in Expo Go by pressing <kbd>i</kbd> or <kbd>a</kbd> in the Terminal UI. Some features may cause your app to throw errors because certain native code is missing, continue reading to learn how you can conditionally skip unsupported APIs.
41
42> Unlike `npx react-native start` the command `npx expo start` hosts an app manifest that dev clients like Expo Go can use to load arbitrary projects. Think of an app manifest like the `<head />` element of an `index.html` but for React Native apps. To view this manifest, visit the dev server URL in your web browser.
43
44## Installing libraries
45
46Ensure your app uses the most compatible library versions for the project's `react-native` version. This means that use `npx expo install` instead of `npm install` to install libraries. Read more about [`npx expo install`](/more/expo-cli/#installation).
47
48## Runtime detection
49
50The easiest way to detect where the JavaScript bundle is running is to check the [`Constants.executionEnvironment`](/versions/latest/sdk/constants/#nativeconstants--properties).
51
52<Terminal cmd={['$ npx expo install expo-constants']} />
53
54```tsx
55import Constants, { ExecutionEnvironment } from 'expo-constants';
56
57// `true` when running in Expo Go.
58const isExpoGo = Constants.executionEnvironment === ExecutionEnvironment.StoreClient;
59```
60
61You can use this boolean to conditionally require custom native code. Here's an example using the library `react-native-blurhash` which is not available in the Expo Go app:
62
63<SnackInline dependencies={['expo-constants', 'react-native-blurhash']}>
64
65```js
66import React from 'react';
67import { StyleSheet, Text, View } from 'react-native';
68import Constants, { ExecutionEnvironment } from 'expo-constants';
69
70// `true` when running in Expo Go.
71const isExpoGo = Constants.executionEnvironment === ExecutionEnvironment.StoreClient;
72
73let Blurhash;
74// Create a fallback for Expo Go
75if (isExpoGo) {
76  Blurhash = props => (
77    <View
78      style={[
79        {
80          backgroundColor: 'lightblue',
81          alignItems: 'center',
82          justifyContent: 'center',
83        },
84        props.style,
85      ]}>
86      <Text>(Blurhash not available)</Text>
87    </View>
88  );
89} else {
90  // Conditionally require this module to prevent Metro from throwing warnings.
91  Blurhash = require('react-native-blurhash').Blurhash;
92}
93
94export default function App() {
95  return <Blurhash blurhash="LGFFaXYk^6#M@-5c,1J5@[or[Q6." style={{ flex: 1 }} />;
96}
97```
98
99</SnackInline>
100
101If you run this code in the Expo Go app, you'll see the fallback view. If you can build this locally with the [Expo CLI run commands](/more/expo-cli/#compiling) then you'll see the native blur hash view.
102
103## Native module detection
104
105Native modules are added to the JavaScript global object at the runtime. This means you can conditionally check if they exist to ensure functionality:
106
107```js
108import { NativeModules } from 'react-native';
109
110const isAvailable = !!NativeModules.MyAnalytics;
111```
112
113The above code snippet ensures the native module _must_ be installed and linked. However, there are two issues with this solution:
114
1151. You need to know the native module name ahead of time.
1162. You likely want an error to be thrown when a native module is missing in a custom build. This helps you determine if there is a native linking issue.
117
118## Optional imports
119
120Optional imports are supported by [Metro bundler](/guides/customizing-metro). They refer to wrapping a `require` statement with a `try/catch` to prevent an error from being thrown when the requested module is missing:
121
122<SnackInline dependencies={['expo-constants', 'react-native-blurhash']}>
123
124```js
125import React from 'react';
126import { View } from 'react-native';
127
128let Blurhash;
129
130try {
131  Blurhash = require('react-native-blurhash').Blurhash;
132} catch {
133  Blurhash = View;
134}
135
136export default function App() {
137  return <Blurhash blurhash="LGFFaXYk^6#M@-5c,1J5@[or[Q6." style={{ flex: 1 }} />;
138}
139```
140
141</SnackInline>
142
143This method is the least reliable because there are several reasons that a `require` statement might throw an error. For example, there could be an internal error, the module could be missing, the native module could be linked incorrectly, and so on. You should avoid using this method.
144
145## Deprecated `.expo` extensions
146
147The `.expo.[js/json/ts/tsx]` extensions to provide Expo Go specific fallbacks are removed in favor of optional imports syntax. For more information, see [Migration off the expo file extension](https://expo.fyi/expo-extension-migration.md#after).
148
149[expo-go]: https://expo.dev/expo-go
150