A guide for integrating Next.js with Expo for the web.
Please open any issues related to Next.js with Expo at expo-cli/issues.
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 its own webpack config so you'll need to start your web projects with the Next.js CLI and not with npx expo start
.
Next.js can only be used with Expo for web, this doesn't provide Server-Side Rendering (SSR) for native apps.
npx create-react-native-app -t with-nextjs
(or npx create-next-app -e with-expo
)yarn next dev
http://localhost:3000/
To get started, create a new project with the template:
npx create-react-native-app -t with-nextjs
yarn next dev
-- start the Next.js projectnpx expo start
-- start the Expo projectThis is for already existing Expo projects.
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.
yarn add -D @expo/next-adapter
npm i --save-dev @expo/next-adapter
yarn next-expo
--customize or -c
--force or -f
yarn next dev
http://localhost:3000/
to see your project!This is for already existing Next.js projects.
This approach is useful if you only want to use Expo components in your web-only project.
yarn add -D @expo/next-adapter
npm i --save-dev @expo/next-adapter
yarn next-expo
--customize or -c
--force or -f
yarn next dev
http://localhost:3000/
to see your project!Optionally you can set the project up manually (not recommended).
Re-export the custom Document
component in the pages/_document.js file of your Next.js project.
react-native-web
styling works.yarn next-expo -c
then select pages/_document.jsmkdir pages; touch pages/_document.js
pages/_document.js
export { default } from '@expo/next-adapter/document';
Create a babel.config.js and use babel-preset-expo
.
yarn next-expo -c
then select babel.config.jsyarn add -D babel-preset-expo
babel.config.js
module.exports = {
presets: ['@expo/next-adapter/babel'],
};
Update the Next.js next.config.js file to support loading React Native and Expo packages:
yarn add -D next-compose-plugins next-transpile-modules
touch next.config.js
next.config.js
const { withExpo } = require('@expo/next-adapter');
const withPlugins = require('next-compose-plugins');
const withTM = require('next-transpile-modules')(['react-native-web']);
const nextConfig = {};
module.exports = withPlugins([withTM, [withExpo, { projectRoot: __dirname }]], nextConfig);
You can now start your Expo web + Next.js project with yarn next dev
🎉
(Formerly ZEIT Now)
This is Vercel's preferred method for deploying Next.js projects to production.
{
"scripts": {
"build": "next build"
}
}
npm i -g vercel
vercel
Fixes
setImmediate is not defined
error.
A 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.
yarn add setimmediate
import 'setimmediate';
If you restart the server this error should go away.
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 images.next-optimized-images
is another good solution that you could check out.Wrap your Next.js configuration object with the image method and the Expo method in your next.config.js:
const { withExpo } = require('@expo/next-adapter');
const withImages = require('next-images');
module.exports = withExpo(
withImages({
projectRoot: __dirname,
})
);
Now restart your project and you should be able to load images!
You can test your config with the following example:
import React from 'react';
import { Image } from 'react-native';
export default function ImageDemo() {
return <Image source={require('./assets/image.png')} style={{ flex: 1 }} />;
}
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:
const { withExpo } = require('@expo/next-adapter');
const withFonts = require('next-fonts');
module.exports = withExpo(
withFonts({
projectRoot: __dirname,
})
);
Now restart your project and you should be able to load fonts!
You can test your config with the following example:
import React, { useEffect, useState } from 'react';
import * as Font from 'expo-font';
import { Text } from 'react-native';
export default function FontDemo() {
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>;
}
Generate static Next.js files into your project.
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 |
-V | --version | output the version number |
The adapter provides a Babel config @expo/next-adapter/babel
to simplify setup.
babel-preset-expo
next/babel
preset.withExpo
Wraps your next.config.js and adds universal platform support.
pageExtensions
which makes webpack resolve .web.js before .js, we call this feature "platform extensions".withUnimodules
from @expo/webpack-config
react-native
to react-native-web
in the browser__DEV__
const { withExpo } = require('@expo/next-adapter');
module.exports = withExpo({
/* next.config.js code */
});
Next.js uses the pages/_document.js file to augment your app's <html>
and <body>
tags. Learn more here.
This adapter provides a default Document
(extended from Next.js's Document) that you can use to skip all of the React Native setup.
AppRegistry
from react-native-web
to start your project.react-native-web
CSS reset.import Document, { style, getInitialProps } from '@expo/next-adapter/document';
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 (
<html>
<Head>
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
// 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;
If you would like to help make Next.js support in Expo better, please feel free to open a PR or submit an issue:
If you have any problems rendering a certain component with SSR then you can submit fixes to the expo/expo repo: