This guide will help you to correctly configure build and test tools to work with React Native for Web. (Alternatively, you can quickly setup a local project using the starter kits listed in the README.)
It is recommended that your application provide a Promise
and Array.from
polyfill.
Webpack is a popular build tool for web apps. Below is an example of how to configure a build that uses Babel to compile your JavaScript for the web.
Create a web/webpack.config.js
file:
// web/webpack.config.js
// This is needed for webpack to compile JavaScript.
// Many OSS React Native packages are not compiled to ES5 before being
// published. If you depend on uncompiled packages they may cause webpack build
// errors. To fix this webpack can be configured to compile to the necessary
// `node_module`.
const babelLoaderConfiguration = {
test: /\.js$/,
// Add every directory that needs to be compiled by Babel during the build
include: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'node_modules/react-native-uncompiled')
],
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
// This aliases 'react-native' to 'react-native-web' and includes only
// the modules needed by the app
plugins: ['react-native-web/babel'],
// The 'react-native' preset is recommended (or use your own .babelrc)
presets: ['react-native']
}
}
};
// This is needed for webpack to import static images in JavaScript files
const imageLoaderConfiguration = {
test: /\.(gif|jpe?g|png|svg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]'
}
}
};
module.exports = {
// ...the rest of your config
module: {
rules: [
babelLoaderConfiguration,
imageLoaderConfiguration
]
},
plugins: [
// `process.env.NODE_ENV === 'production'` must be `true` for production
// builds to eliminate development checks and reduce build size. You may
// wish to include additional optimizations.
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
],
resolve: {
// If you're working on a multi-platform React Native app, web-specific
// module implementations should be written in files using the extension
// `.web.js`.
extensions: [ '.web.js', '.js' ]
}
}
To run in development:
./node_modules/.bin/webpack-dev-server -d --config web/webpack.config.js --inline --hot --colors
To build for production:
./node_modules/.bin/webpack -p --config web/webpack.config.js
Please refer to the Webpack documentation for more information on configuration.
Create a index.web.js
file (or simply index.js
for web-only apps).
Rendering using AppRegistry
:
// index.web.js
import App from './src/App';
import React from 'react';
import ReactNative, { AppRegistry } from 'react-native';
// register the app
AppRegistry.registerComponent('App', () => App);
AppRegistry.runApplication('App', {
initialProps: {},
rootTag: document.getElementById('react-app')
});
Rendering within existing web apps is also possible using ReactNative
:
import AppHeader from './src/AppHeader';
import React from 'react';
import ReactNative from 'react-native';
// use .hydrate if hydrating a SSR app
ReactNative.render(<AppHeader />, document.getElementById('react-app-header'))
And finally, react-native-web
components will also be rendering within a tree
produced by calling ReactDOM.render
(i.e., an existing web app), but
otherwise it is not recommended.
Server-side rendering is supported using the AppRegistry
:
import App from './src/App';
import ReactDOMServer from 'react-dom/server'
import ReactNative, { AppRegistry } from 'react-native'
// register the app
AppRegistry.registerComponent('App', () => App)
// prerender the app
const { element, stylesheets } = AppRegistry.getApplication('App', { initialProps });
const initialHTML = ReactDOMServer.renderToString(element);
const initialStyles = stylesheets.map((sheet) => ReactDOMServer.renderToStaticMarkup(sheet)).join('\n');
// construct HTML document
const document = `
<!DOCTYPE html>
<html>
<head>
${initialStyles}
</head>
<body>
${initialHTML}
`
Minor platform differences can use the Platform
module.
import { Platform } from 'react-native';
const styles = StyleSheet.create({
height: (Platform.OS === 'web') ? 200 : 100,
});
More significant platform differences should use platform-specific files (see
the webpack configuration above for resolving *.web.js
files):
For example, with the following files in your project:
MyComponent.android.js
MyComponent.ios.js
MyComponent.web.js
And the following import:
import MyComponent from './MyComponent';
React Native will automatically import the correct variant for each specific target platform.
Jest can be configured to improve snapshots
of react-native-web
components.
{
"snapshotSerializers": [ "enzyme-to-json/serializer", "react-native-web/jest/serializer" ]
}
Jest also needs to map react-native
to react-native-web
(unless you are
using Babel with the react-native-web/babel
plugin).
{
"moduleNameMapper": {
"react-native": "<rootDir>/node_modules/react-native-web"
}
}
Please refer to the Jest documentation for more information.