Blog
React Native For Web: Merging the worlds of Mobile and Web
One of the hardest decisions to make when starting a new app is which platforms to target. A mobile app gives you more control and better performance but isn’t as universal as the web.What about trying to build a mobile app and a responsive web app? Ultimately, the best experience for your customers is for your app to work everywhere, but the development and maintenance costs of that can be prohibitive.We have seen how React Native can help you make iOS and Android apps with a shared codebase, without sacrifices in quality. But what about the web? React-native-web is one of the most amazing ideas I’ve stumbled upon in a while. For UI developers, it makes a longtime dream a reality: the ability to create an application that runs on both phones and browsers with just one codebase.React Native as a Universal UI languageYou might be thinking, “Wait! doesn’t React already work on the web?” You wouldn’t be wrong. Unfortunately, traditional React and React Native build on a different set of primitives. React uses <div>, <p> and <input>, whereas React Native uses <View>, <Text> and <TextInput>. There are good historical reasons for this since the building blocks of a web page and of a mobile app are quite different. Nonetheless, it would be great if we could use a single set of shared components.One of the reason what really makes React Native superior to React for creating universal apps — is that React Native is a pure UI language. React Native for Web’s solution is to provide browser-compatible implementations of React Native’s components — meaning, for example, that the <View> of React Native has a DOM-based version that knows how to render to a <div>. While not every React Native component is supported, enough of them are that you could (hopefully) share the majority of your codebase.Nonetheless, it’s possible to translate React Native primitives to the DOM language by using HTML tags — that (and more) is what react-native-web does for us.React Native for WebRepository — https://github.com/necolas/react-native-webCompatibility: React Native >= 0.63.“React Native for Web” makes it possible to run React Native components and APIs on the web using React DOM.High-quality web interfaces: makes it easy to create fast, adaptive web UIs in JavaScript. It provides native-quality interactions, support for multiple input modes (touch, mouse, keyboard), optimized vendor-prefixed styles, built-in support for RTL layout, built-in accessibility, and integrates with React Dev Tools.Write once, render anywhere: interoperates with existing React DOM components and is compatible with the majority of the React Native API. You can develop new components for native and web without rewriting existing code. React Native for Web can also render to HTML and critical CSS on the server using Node.js.Big Giants using React Native for Web in production?Twitter, Expo, Major League Soccer, Flipkart, Uber, The Times, DataCamp.Getting startedThis guide will help you render components and applications with React Native for Web.If you’re not familiar with setting up a new React web project, please refer to the React documentation. Installnpm install react react-dom react-native-webRecommended starter kitsThere are two ways to get started with a new React-native-web project. You can use either npm or yarn.ExpoExpo is a framework and a platform for universal React applications. Expo for Web uses React Native for Web and provides dozens of additional cross-platform APIs.npm install expo-cli –globalexpo init my-appcd my-appexpo startCreate React AppCreate React App is a basic way to setup a simple, web-only React app with built-in support for aliasing react-native-web to react-native. However, it’s generally recommended that you use Expo.npx create-react-app my-appcd my-appnpm install react-native-webnpm startStandalone configurationsConfiguring a module bundlerIf you have a custom setup, you may choose to configure your module bundler to alias the package to react-native.For example, modify your webpack configuration as follows:// webpack.config.jsmodule.exports = { // …the rest of your config resolve: { alias: { ‘react-native$’: ‘react-native-web’ } }}Configuring BabelBabel supports module aliasing using babel-plugin-module-resolver{ “plugins”: [ [“module-resolver”, { “alias”: { “^react-native$”: “react-native-web” } }] ]}Configuring JestJest can be configured using the provided preset. This will map react-native to react-native-web and provide appropriate mocks:{ “preset”: “react-native-web”}Please refer to the Jest documentation for more information.Configuring FlowFlow can be configured to understand the aliased module:[options]module.name_mapper=’^react-native$’ -> ‘react-native-web’You may also need to include a custom libdef (example) in your config.Configuring Node.jsNode.js can alias react-native to react-native-web using module-alias. This is useful if you want to pre-render the app (e.g., server-side rendering or build-time rendering).// Install the `module-alias` package as a dependency firstconst moduleAlias = require(“module-alias”);moduleAlias.addAliases({ “react-native”: require.resolve(“react-native-web”),});moduleAlias();Configure StorybooksWhy would I want a storybook with react-native-web?If you are using Storybook, having an explorer to see how to render your native mobile components matters to you. Why would it be different within the web?Start by adding storybook/react to your project:npm install @storybook/react –save-devNow create a folder storybook-web at the root of your project. This is where we are gonna put all our configuration for the storybook web.Create a file main.js in the folder storybook-web which contain the following code:module.exports = { stories: [‘../src/components/**/*.stories.js’], webpackFinal: (config) => { config.resolve.alias = { …(config.resolve.alias || {}), // Transform all direct `react-native` imports to `react-native-web` ‘react-native$’: ‘react-native-web’, ‘@storybook/react-native’: ‘@storybook/react’, }; config.resolve.extensions = [‘.web.js’, ‘.js’, ‘.json’]; // mutate babel-loader config.module.rules[0].use[0].options.plugins.push([‘react-native-web’, { commonjs: true }]); return config; },};When building on the web, this will replace every react-native import into react-native-web import and doing the same with @storybook/react-native and @storybook/react.If you use some native libs which are not supported by react-native-web, you can easily create your own alias.Let’s now create the file addons.js which will add the addons you normally use on storybook mobile:import ‘@storybook/addon-actions/register’;import ‘@storybook/addon-knobs/register’;import ‘@storybook/addon-links/register’;The minimal configuration for our project is set! We just need to add a command in the package.json to launch a storybook on the web!In the scripts object, add the following command:“storybook:web”: “ln -s ../@storybook/react/bin/index.js storybookweb && mv storybookweb node_modules/.bin && storybookweb -p 6006 –config-dir storybook-web”A quick explanation: @storybook/react-native and @storybook/react to have the same link name to launch their binary: start-storybook.To be able to differentiate them, we have to create a new link for @storybook/react. This is what we do here:ln -s ../@storybook/react/bin/index.js storybookwebI named it storybook web but you can change it as you want.After created, we move it in node_modules/.bin and we use our new command to launch @storybook/react, specifying the configuration directory:mv storybookweb node_modules/.bin && storybookweb -p 6006 –config-dir storybook-web”Now you can launch yarn storybook: web and enjoy your components on the web!About the AuthorShikher Mishra, a brilliant software developer aims to bring technology a bit closer to non-tech savvy people by writing blogs that simplify and educate.
Learn More >