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