react-native-web-webpack-template

Basic starter template, showing how to correctly configure React Native Web using React Native Cli, using webpack and typescript.

MIT License

Stars
2
Committers
1

React Native Cli - Webpack

Description

Basic starter template, showing how to correctly configure React Native Web using React Native Cli, using webpack and typescript.

Installation

First install React Native, if you already have it installed, skip this step.

npx @react-native-community/cli@latest init web

Run

cd web/

Now install react native web and its dependencies

yarn add react-dom react-native-web

The Babel plugin is recommended for build-time optimizations.

yarn add -D babel-plugin-react-native-web

Install webpack dependencies

yarn add -D webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader babel-plugin-module-resolver url-loader @svgr/webpack

Configuration files

Add the necessary scripts to run the project to your package.json

"build:web": "rm -rf dist/ && webpack --mode=production --config webpack.config.js --progress",
"web": "webpack serve --mode=development --config webpack.config.js --progress",
"scripts": {
  "android": "react-native run-android",
  "ios": "react-native run-ios",
  "build:web": "rm -rf dist/ && webpack --mode=production --config webpack.config.js --progress", // This line for build project
  "web": "webpack serve --mode=development --config webpack.config.js --progress", // This line for dev mode
  "lint": "eslint .",
  "start": "react-native start",
  "test": "jest"
},

Copy the code to App.tsx

If you want to have separate web files, create an App.web.tsx file and replace all the values ​​from the following steps.

import {
  Linking,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from "react-native";

function App() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Hello World!</Text>
      <Text style={styles.subTitle}>React Native Web</Text>
      <TouchableOpacity style={styles.button}>
        <Text style={styles.buttonText}>Press Me</Text>
      </TouchableOpacity>
      <Text>
        Created by:{" "}
        <TouchableOpacity
          onPress={() => {
            Linking.openURL("https://github.com/gabriel-logan");
          }}
        >
          <Text style={styles.link}>Gabriel Logan</Text>
        </TouchableOpacity>{" "}
        using{" "}
        <TouchableOpacity
          onPress={() => {
            Linking.openURL("https://necolas.github.io/react-native-web/");
          }}
        >
          <Text style={styles.link}>React Native Web</Text>
        </TouchableOpacity>
        , Webpack and TypeScript
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    alignItems: "center",
  },
  button: {
    backgroundColor: "#ADBDFF",
    padding: 10,
    marginVertical: 20,
    borderRadius: 5,
  },
  buttonText: {
    fontSize: 20,
  },
  title: {
    fontSize: 40,
  },
  subTitle: {
    fontSize: 20,
  },
  paragraph: {
    fontSize: 16,
  },
  link: {
    color: "blue",
    textDecorationLine: "underline",
  },
});

export default App;

Create a file called index.html in the root folder of your project

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <meta name="description" content="React Native Web" />
    <title>React Native Web</title>
    <style>
      #app-root {
        display: flex;
        flex: 1 1 100%;
        height: 100vh;
      }
    </style>
  </head>
  <body>
    <div id="app-root"></div>
  </body>
</html>

Now create a file in the root folder named index.web.js

Paste the code below

import { AppRegistry } from "react-native";

import App from "./App";
import { name as appName } from "./app.json";
if (module.hot) {
  module.hot.accept();
}
AppRegistry.registerComponent(appName, () => App);
AppRegistry.runApplication(appName, {
  initialProps: {},
  rootTag: document.getElementById("app-root"),
});

Now create a webpack configuration file webpack.config.js in the root folder

and paste the code below

const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
const webpack = require("webpack");

const appDirectory = path.resolve(__dirname);
const { presets, plugins } = require(`${appDirectory}/babel.config.web.js`);

const compileNodeModules = [
  // Add every react-native package that needs compiling
  // 'react-native-gesture-handler',
].map((moduleName) => path.resolve(appDirectory, `node_modules/${moduleName}`));

const babelLoaderConfiguration = {
  test: /\.js$|tsx?$/,
  // Add every directory that needs to be compiled by Babel during the build.
  include: [
    path.resolve(__dirname, "index.web.js"), // Entry to your application
    path.resolve(__dirname, "App.tsx"), // Change this to your main App file
    path.resolve(__dirname, "src"),
    ...compileNodeModules,
  ],
  use: {
    loader: "babel-loader",
    options: {
      cacheDirectory: true,
      presets,
      plugins,
    },
  },
};

const svgLoaderConfiguration = {
  test: /\.svg$/,
  use: [
    {
      loader: "@svgr/webpack",
    },
  ],
};

const imageLoaderConfiguration = {
  test: /\.(gif|jpe?g|png)$/,
  use: {
    loader: "url-loader",
    options: {
      name: "[name].[ext]",
    },
  },
};

/** @type {import("webpack").Configuration} */
module.exports = {
  entry: {
    app: path.join(__dirname, "index.web.js"),
  },
  output: {
    path: path.resolve(appDirectory, "dist"),
    publicPath: "/", // Using ./ for the github pages, change to / for local
    filename: "rnw.bundle.js",
  },
  resolve: {
    extensions: [".web.tsx", ".web.ts", ".tsx", ".ts", ".web.js", ".js"],
    alias: {
      "react-native$": "react-native-web",
    },
  },
  module: {
    rules: [
      babelLoaderConfiguration,
      imageLoaderConfiguration,
      svgLoaderConfiguration,
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "index.html"),
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.DefinePlugin({
      // See: https://github.com/necolas/react-native-web/issues/349
      __DEV__: JSON.stringify(true),
    }),
  ],
};

Create a new babel file so there are no conflicts with the mobile version

Add settings to babel.config.web.js

module.exports = {
  presets: ["module:@react-native/babel-preset"],
  plugins: [
    [
      "module-resolver",
      {
        alias: {
          "^react-native$": "react-native-web",
        },
      },
    ],
    "react-native-web",
  ],
};

If you need to create tests using jest add the configuration below to your test file

moduleNameMapper: {
   "^react-native$": "react-native-web",
},

Additional

To copy public static files to production, add an additional configuration in webpack

yarn add -D copy-webpack-plugin

Import the configuration into your webpack configuration file

const CopyWebpackPlugin = require("copy-webpack-plugin");

and finally add the configuration in the plugin part

plugins: [
  new HtmlWebpackPlugin({
    template: path.join(__dirname, "index.html"),
  }),
  new webpack.HotModuleReplacementPlugin(),
  new webpack.DefinePlugin({
    // See: https://github.com/necolas/react-native-web/issues/349
    __DEV__: JSON.stringify(true),
  }),
  new CopyWebpackPlugin({ // ADD THIS LINE
    patterns: [{ from: "public", to: "" }],
  }),
],

With these settings, all files inside the public folder will be compiled together to the dist folder.

Now you can add a favicon for example in the public/assets/favicon.png folder

And add it to your index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <meta name="description" content="React Native Web" />
    <link rel="icon" href="./assets/favicon.png" />
    <title>React Native Web</title>
    <style>
      #app-root {
        display: flex;
        flex: 1 1 100%;
        height: 100vh;
      }
    </style>
  </head>
  <body>
    <div id="app-root"></div>
  </body>
</html>

Now you will see your favicon appearing in development mode as well as in production.

Done

Your react native web project configured with webpack is ready for the initial kickstart

Thanks for reading

Created by: Gabriel Logan using React Native Web

@react-navigation

Follow the manual installation documentation for @react-navigation/native which is required to use React Navigation

https://reactnavigation.org/docs/getting-started/

Then use one of the Navigators you intend to use.

For example: Native Stack Navigator

https://reactnavigation.org/docs/native-stack-navigator

Read the information about web support

https://reactnavigation.org/docs/web-support

The Example without React Navigation can be found in the docs folder

If you want to test with React Navigation, you can git clone the main repository.

License

MIT