Warning: Support for Next.js is experimental. Please open an issue at expo-cli/issues if you encountered any problems.
Next.js 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 to handle the configuration and integration of the tools.
Using Expo with Next.js means you can share all of your existing components and APIs across your mobile and web. Next.js has it's own Webpack config so you'll need to start your web projects with the next-cli and not with expo start:web.
💡 Next.js can only be used with Expo for web, this doesn't provide Server-Side Rendering (SSR) for native apps.
In 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.
Bootstrap your project with Expo
Install the CLI: npm i -g expo-cli
Create a project: expo init --template blank
cd into the project
Install the adapter:
yarn:yarn add @expo/next-adapter
npm: npm i --save @expo/next-adapter
Add Next.js support: yarn next-expo
Always commit your changes first!
You can optionally choose which customizations you want to do with --customize or -c
Force reload changes with --force or -f
Start the project with yarn next dev
Go to http://localhost:3000/ to see your project!
Next.js projects with Expo
This approach is useful if you only want to use Expo components in your web-only project.
Bootstrap your project with Next.js
Create a project: npx create-next-app
Install the adapter:
yarn:yarn add @expo/next-adapter
npm: npm i --save @expo/next-adapter
Add Next.js support: yarn next-expo
Always commit your changes first!
You can optionally choose which customizations you want to do with --customize or -c
Force reload changes with --force or -f
Start the project with yarn next dev
Go to http://localhost:3000/ to see your project!
Manual setup
Optionally you can set the project up manually (not recommended).
Instructions
Re-export the custom Document component in the pages/_document.js file of your Next.js project.
This will ensure react-native-web styling works.
You can run yarn next-expo -c then select pages/_document.js
Or you can create the file - mkdir pages; touch pages/_document.js
You can now start your Expo web + Next.js project with yarn next dev 🎉
Guides
Deploy to Now
This is Zeit's preferred method for deploying Next.js projects to production.
Add a build script to your package.json
{
"scripts": {
"build": "next build"
}
}
Install the Now CLI: npm i -g now
Deploy to Now: now
Image support
By 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:
Install the plugin - yarn add next-images
next-images injects a Webpack loader to handle fonts.
By 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.
Install the plugin - yarn add next-fonts
next-fonts injects a Webpack loader to handle fonts.
Wrap the font method with the Expo method in your next.config.js:
The order is important because Expo can mix in the location of vector icons to the existing font loader.
Now restart your project and you should be able to load fonts!
You can test your config with the following example:
Show Example
import React,{ useEffect, useState }from'react';import*as Font from'expo-font';import{ Text }from'react-native';exportdefaultfunctionFontDemo(){const[loaded, setLoaded]=useState(false);useEffect(()=>{(async()=>{try{await Font.loadAsync({// You can get this font on Github: https://shorturl.at/chEHS'space-mono':require('./assets/SpaceMono-Regular.ttf'),});}catch({ message }){// This will be called if something is broken
console.log(`Error loading font: ${message}`);}finally{setLoaded(true);}})();},[]);if(!loaded)return<Text>Loading fonts...</Text>;return<Text style={{ fontFamily:'space-mono'}}>Hello from Space Mono</Text>;}
Offline support
Unlike the default Expo for web workflow, Workbox and PWA are not supported out of the box. Here you can learn how to use the plugin next-offline to get offline support in your Next.js + Expo app.
Instructions
Install next-offline to emulate Expo PWA features: yarn add next-offline
Configure your Next.js project to use expo-notifications in the browser:
We inject a custom service worker so we'll need to change what Workbox names their service worker (it must be workbox-service-worker.js).
next.config.js
const withOffline =require('next-offline');const{ withExpo }=require('@expo/next-adapter');// If you didn't install next-offline, then simply delete this method and the import.
module.exports =withOffline({
workboxOpts:{
swDest:'workbox-service-worker.js',/* changing any value means you'll have to copy over all the defaults *//* next-offline */
globPatterns:['static/**/*'],
globDirectory:'.',
runtimeCaching:[{
urlPattern:/^https?.*/,
handler:'NetworkFirst',
options:{
cacheName:'offlineCache',
expiration:{
maxEntries:200,},},},],},...withExpo({
projectRoot: __dirname,}),});
Copy the Expo service worker into your project's public folder: mkdir public; cp node_modules/@expo/next-adapter/service-worker.js public/service-worker.js
You can now test your project in production mode using the following: yarn next build && yarn next export && serve -p 3000 ./out
Using a custom server
If you have a complex project that requires custom server control then you can extend the default server to control hosting.
Instructions
Create a custom server to host your service worker:
server.js
You may want to intercept server requests, this will allow for that:
server.js
const{ createServerAsync }=require('@expo/next-adapter');createServerAsync(projectRoot,{handleRequest(res, req){const parsedUrl =parse(req.url,true);const{ pathname }= parsedUrl;// handle GET request to /cool-file.pngif(pathname ==='/cool-file.png'){const filePath =join(__dirname,'.next', pathname);
app.serveStatic(req, res, filePath);// Return true to prevent the default handlerreturntrue;}},}).then(({ server, app })=>{const port =3000;
server.listen(port,()=>{
console.log(`> Ready on http://localhost:${port}`);});});
Web push notifications support
With the regular expo start:web or expo start --web commands web push notifications are supported without any additional configuration. To get this same functionality working with Next.js you'll need to configure a few things.
Instructions
To use it with other services such as ZEIT Now, you would need appropriate configuration to
let /service-worker.js serve the file content of /public/service-worker.js, and
let /workbox-service-worker.js serve the file content of a service worker, which be:
/public/workbox-service-worker.js (which will by default be a blank file) if you do not want to use any other service worker, or
/_next/public/workbox-service-worker.js if you are using next-offline, or
your own service worker file.
Here is an example now.json configuration file:
{
"version": 2,
"routes": [
{
"src": "/service-worker.js",
"dest": "/public/service-worker.js",
"headers": {
"cache-control": "public, max-age=43200, immutable",
"Service-Worker-Allowed": "/"
}
},
// If you are using next-offline, change the object below according to their guide.
{
"src": "/workbox-service-worker.js",
"dest": "/public/workbox-service-worker.js",
"headers": {
"cache-control": "public, max-age=43200, immutable",
"Service-Worker-Allowed": "/"
}
}
]
}
API
CLI
Generate static Next.js files into your project.
⚙️ CLI Options
For more information run yarn next-expo --help (or -h)
Shortcut
Flag
Description
-f
--force
Allows replacing existing files
-c
--customize
Select template files you want to add to your project
If 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.
import { getInitialProps } from '@expo/next-adapter/document';
import Document, { Head, Main, NextScript } from 'next/document';
import React from 'react';
class CustomDocument extends Document {
render() {
return (
);
}
}
// Import the getInitialProps method and assign it to your component to ensure the react-native-web styles are used.
CustomDocument.getInitialProps = getInitialProps;
// OR...
CustomDocument.getInitialProps = async props => {
const result = await getInitialProps(props);
// Mutate result...
return result;
};
export default CustomDocument;
Server
@expo/next-adapter provides you with a light-weight and easy to use http server for controlling how your project is hosted. The main reason for using this is to forward the requests for service workers to the static folder where Next.js expects them to be.
Limitations or differences comparing to the default Expo for Web
Unlike the default Expo for Web, Workbox and PWA are not supported by default. Use Next.js plugins such as next-offline instead. Learn more here.
You might need to use the next-transpile-modules plugin to transpile certain third-party modules in order for them to work (such as Emotion). An easy but fragile way to do this is by defining the package name in your app.json under expo.web.build.babel.include (it's experimental because that's a really deeply nested object).
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 for this.
Contributing
If you would like to help make Next.js support in Expo better, please feel free to open a PR or submit an issue: