xref: /expo/docs/pages/guides/using-nextjs.mdx (revision c615fa2e)
1---
2title: Using Next.js with Expo for Web
3sidebar_title: Using Next.js
4description: A guide for integrating Next.js with Expo for the web.
5---
6
7import { Collapsible } from '~/ui/components/Collapsible';
8
9> Please open any issues related to Next.js with Expo at [expo-cli/issues](https://github.com/expo/expo-cli/issues).
10
11[Next.js][nextjs] is a React framework that provides simple page-based routing as well as server-side rendering. To use Next.js with Expo for web we recommend that you use a library called [`@expo/next-adapter`][next-adapter] to handle the configuration and integration of the tools.
12
13Using Expo with Next.js means you can share all of your existing components and APIs across your mobile and web. Next.js has its own webpack config so **you'll need to start your web projects with the Next.js CLI and not with `npx expo start`.**
14
15> Next.js can only be used with Expo for web, this doesn't provide Server-Side Rendering (SSR) for native apps.
16
17## TL;DR:
18
19- Init: `npx create-react-native-app -t with-nextjs` (or `npx create-next-app -e with-expo`)
20- Start: `yarn next dev`
21- Open: `http://localhost:3000/`
22
23## Setup
24
25To get started, create a new project with [the template](https://github.com/expo/examples/tree/master/with-nextjs):
26
27```sh
28npx create-react-native-app -t with-nextjs
29```
30
31- **Web**: `yarn next dev` -- start the Next.js project
32- **Native**: `npx expo start` -- start the Expo project
33
34### Add Next.js to Expo projects
35
36> This is for already existing Expo projects.
37
38In this approach you would be using SSR for web in your universal project. This is the recommended path because it gives you full access to the features of Expo and Next.js.
39
40<Collapsible summary="Instructions">
41
42- Install the adapter:
43  - **yarn:** `yarn add -D @expo/next-adapter`
44  - npm: `npm i --save-dev @expo/next-adapter`
45- Add Next.js support: `yarn next-expo`
46  - Always commit your changes first!
47  - You can optionally choose which customizations you want to do with `--customize or -c`
48  - Force reload changes with `--force or -f`
49- Start the project with `yarn next dev`
50  - Go to `http://localhost:3000/` to see your project!
51
52</Collapsible>
53
54### Add Expo to Next.js projects
55
56> This is for already existing Next.js projects.
57
58This approach is useful if you only want to use Expo components in your web-only project.
59
60<Collapsible summary="Instructions">
61
62- Install the adapter:
63  - **yarn:** `yarn add -D @expo/next-adapter`
64  - npm: `npm i --save-dev @expo/next-adapter`
65- Add Next.js support: `yarn next-expo`
66  - Always commit your changes first!
67  - You can optionally choose which customizations you want to do with `--customize or -c`
68  - Force reload changes with `--force or -f`
69- Start the project with `yarn next dev`
70  - Go to `http://localhost:3000/` to see your project!
71
72</Collapsible>
73
74### Manual setup
75
76Optionally you can set the project up manually (not recommended).
77
78<Collapsible summary="Instructions">
79
80- Re-export the custom `Document` component in the **pages/\_document.js** file of your Next.js project.
81
82  - This will ensure `react-native-web` styling works.
83  - You can run `yarn next-expo -c` then select **pages/\_document.js**
84  - Or you can create the file - `mkdir pages; touch pages/_document.js`
85
86  **pages/\_document.js**
87
88  ```js
89  export { default } from '@expo/next-adapter/document';
90  ```
91
92- Create a **babel.config.js** and use [`babel-preset-expo`](https://github.com/expo/expo/tree/main/packages/babel-preset-expo).
93
94  - You can run `yarn next-expo -c` then select **babel.config.js**
95  - Or you can You may have installed this earlier with `yarn add -D babel-preset-expo`
96
97  **babel.config.js**
98
99  ```js
100  module.exports = {
101    presets: ['@expo/next-adapter/babel'],
102  };
103  ```
104
105- Update the Next.js **next.config.js** file to support loading React Native and Expo packages:
106
107  - yarn add -D next-compose-plugins next-transpile-modules
108
109  - `touch next.config.js`
110
111  **next.config.js**
112
113  ```js
114  const { withExpo } = require('@expo/next-adapter');
115  const withPlugins = require('next-compose-plugins');
116  const withTM = require('next-transpile-modules')(['react-native-web']);
117
118  const nextConfig = {};
119
120  module.exports = withPlugins([withTM, [withExpo, { projectRoot: __dirname }]], nextConfig);
121  ```
122
123- You can now start your Expo web + Next.js project with `yarn next dev` ��
124
125</Collapsible>
126
127## Guides
128
129### Deploy to Vercel
130
131(Formerly ZEIT Now)
132
133This is Vercel's preferred method for deploying Next.js projects to production.
134
135- Add a **build** script to your **package.json**
136  ```json
137  {
138    "scripts": {
139      "build": "next build"
140    }
141  }
142  ```
143- Install the Vercel CLI: `npm i -g vercel`
144- Deploy to Vercel: `vercel`
145
146### Polyfill setImmediate
147
148> Fixes `setImmediate is not defined` error.
149
150A lot of libraries in the React ecosystem use the `setImmediate()` API (like `react-native-reanimated`), which Next.js doesn't polyfill by default. To fix this you can polyfill it yourself.
151
152- Install: `yarn add setimmediate`
153- Import in **pages/\_app.js**, at the top of the file:
154  ```js
155  import 'setimmediate';
156  ```
157
158If you restart the server this error should go away.
159
160- [Related issue and solution](https://github.com/expo/expo/issues/7996).
161
162### Image support
163
164By default Next.js won't load your statically imported images (images that you include in your project with `require('./path/to/image.png')`) like an Expo project will. If you want to load static images into your `<Image />` components or use `react-native-svg` then you can do the following:
165
166- Install the plugin - `yarn add next-images`
167  - [`next-images`][next-images] injects a webpack loader to handle images.
168  - [`next-optimized-images`][next-optimized-images] is another good solution that you could check out.
169- Wrap your Next.js configuration object with the image method and the Expo method in your **next.config.js**:
170
171  ```js
172  const { withExpo } = require('@expo/next-adapter');
173  const withImages = require('next-images');
174
175  module.exports = withExpo(
176    withImages({
177      projectRoot: __dirname,
178    })
179  );
180  ```
181
182- Now restart your project and you should be able to load images!
183
184You can test your config with the following example:
185
186<Collapsible summary="Show Example">
187
188```js
189import React from 'react';
190import { Image } from 'react-native';
191
192export default function ImageDemo() {
193  return <Image source={require('./assets/image.png')} style={{ flex: 1 }} />;
194}
195```
196
197</Collapsible>
198
199[next-images]: https://github.com/twopluszero/next-images
200[next-optimized-images]: https://github.com/cyrilwanner/next-optimized-images
201
202### Font support
203
204By default Next.js doesn't support static assets like an Expo project. Because this is the intended functionality of Next.js, `@expo/next-adapter` doesn't add font support by default. If you want to use libraries like `expo-font`, `@expo/vector-icons`, or `react-native-vector-icons` you'll need to change a few things.
205
206- Install the plugin - `yarn add next-fonts`
207  - [`next-fonts`][next-fonts] injects a webpack loader to handle fonts.
208- Wrap the font method with the Expo method in your **next.config.js**:
209
210  - The order is important because Expo can mix in the location of vector icons to the existing font loader.
211
212  ```js
213  const { withExpo } = require('@expo/next-adapter');
214  const withFonts = require('next-fonts');
215
216  module.exports = withExpo(
217    withFonts({
218      projectRoot: __dirname,
219    })
220  );
221  ```
222
223- Now restart your project and you should be able to load fonts!
224
225You can test your config with the following example:
226
227<Collapsible summary="Show Example">
228
229```js
230import React, { useEffect, useState } from 'react';
231import * as Font from 'expo-font';
232import { Text } from 'react-native';
233
234export default function FontDemo() {
235  const [loaded, setLoaded] = useState(false);
236
237  useEffect(() => {
238    (async () => {
239      try {
240        await Font.loadAsync({
241          // You can get this font on GitHub: https://shorturl.at/chEHS
242          'space-mono': require('./assets/SpaceMono-Regular.ttf'),
243        });
244      } catch ({ message }) {
245        // This will be called if something is broken
246        console.log(`Error loading font: ${message}`);
247      } finally {
248        setLoaded(true);
249      }
250    })();
251  }, []);
252
253  if (!loaded) return <Text>Loading fonts...</Text>;
254
255  return <Text style={{ fontFamily: 'space-mono' }}>Hello from Space Mono</Text>;
256}
257```
258
259</Collapsible>
260
261[next-fonts]: https://github.com/rohanray/next-fonts
262
263## API
264
265### CLI
266
267Generate static Next.js files into your project.
268
269#### CLI Options
270
271For more information run `yarn next-expo --help` (or `-h`)
272
273| Shortcut | Flag          | Description                                           |
274| -------- | ------------- | ----------------------------------------------------- |
275| `-f`     | `--force`     | Allows replacing existing files                       |
276| `-c`     | `--customize` | Select template files you want to add to your project |
277| `-V`     | `--version`   | output the version number                             |
278
279### Babel
280
281The adapter provides a Babel config [`@expo/next-adapter/babel`](https://github.com/expo/expo-cli/blob/master/packages/next-adapter/src/babel.ts) to simplify setup.
282
283- Always use the universal [`babel-preset-expo`](https://github.com/expo/expo/tree/main/packages/babel-preset-expo)
284  - Provides React Native support for all platforms that Expo supports (web, iOS, Android)
285- When running in the browser, also use `next/babel` preset.
286
287### Config
288
289#### `withExpo`
290
291Wraps your [**next.config.js**](https://nextjs.org/docs#custom-configuration) and adds universal platform support.
292
293- Defines a custom `pageExtensions` which makes webpack resolve **.web.js** before **.js**, we call this feature "platform extensions".
294- Wraps the webpack config in `withUnimodules` from `@expo/webpack-config`
295  - Makes Babel target all Expo, and React Native packages that you've installed
296  - Aliases `react-native` to `react-native-web` in the browser
297  - Defines the platform constants you get in React Native like `__DEV__`
298
299```js
300const { withExpo } = require('@expo/next-adapter');
301
302module.exports = withExpo({
303  /* next.config.js code */
304});
305```
306
307### Document
308
309Next.js uses the **pages/\_document.js** file to augment your app's `<html>` and `<body>` tags. Learn more [here](https://nextjs.org/docs#custom-document).
310
311This adapter provides a default `Document` (extended from Next.js's Document) that you can use to skip all of the React Native setup.
312
313- Registers your app with `AppRegistry` from `react-native-web` to start your project.
314- Implements the `react-native-web` CSS reset.
315
316```js
317import Document, { style, getInitialProps } from '@expo/next-adapter/document';
318```
319
320#### Customizing the Document
321
322If you need more control you can import then recompose the `Document` how you like. This is good for augmenting the `<head />` element or mixing your own styles.
323
324```tsx
325import { getInitialProps } from '@expo/next-adapter/document';
326import Document, { Head, Main, NextScript } from 'next/document';
327import React from 'react';
328
329class CustomDocument extends Document {
330  render() {
331    return (
332      <html>
333        <Head>
334          <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
335        </Head>
336        <body>
337          <Main />
338          <NextScript />
339        </body>
340      </html>
341    );
342  }
343}
344
345// Import the getInitialProps method and assign it to your component to ensure the react-native-web styles are used.
346CustomDocument.getInitialProps = getInitialProps;
347
348// OR...
349
350CustomDocument.getInitialProps = async props => {
351  const result = await getInitialProps(props);
352  // Mutate result...
353  return result;
354};
355
356export default CustomDocument;
357```
358
359## Limitations or differences comparing to the default Expo for Web
360
361- To get PWA support, use Next.js plugins such as [next-offline][next-offline] instead. Learn more [here][next-pwa].
362- You might need to use the [next-transpile-modules](https://github.com/martpie/next-transpile-modules) plugin to transpile certain third-party modules in order for them to work (such as Emotion).
363- Only the Next.js default page-based routing is supported. You'll need to use a completely different routing solution to do native navigation. We strongly recommend [react-navigation](https://reactnavigation.org/) for this.
364
365## Contributing
366
367If you would like to help make Next.js support in Expo better, please feel free to open a PR or submit an issue:
368
369- [@expo/next-adapter][next-adapter]
370
371If you have any problems rendering a certain component with SSR then you can submit fixes to the expo/expo repo:
372
373- [Expo SDK packages][expo-packages]
374
375{/* Footer */}
376
377[expo-packages]: https://github.com/expo/expo/tree/main/packages
378[nextjs]: https://nextjs.org/
379[next-adapter]: https://github.com/expo/expo-cli/tree/main/packages/next-adapter
380[next-docs]: https://nextjs.org/docs
381[custom-document]: https://nextjs.org/docs#custom-document
382[next-offline]: https://github.com/hanford/next-offline
383[next-pwa]: https://nextjs.org/features/progressive-web-apps
384[next-transpile-modules]: https://github.com/martpie/next-transpile-modules
385