skip to Main Content

I’m new to React Native. So I want to implement react-native-web to an existing apps (Android and iOS) and I following this tutorial. And I run the web with npm run web and it can run but only show blank page (the App.tsx not there). I wonder if I miss something or misconfig my code. Below is the structure and code.

Project structure

- Root
  -> android folder
  -> ios folder
  -> src folder
    -> main app & component
  -> public folder (empty)
  -> App.web.tsx (sample tsx file for testing the web)
  -> index.html
  -> index.web.js
  -> webpack.config.js
  -> other json, config, js file

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" />
    <title>RN Web</title>
    <style>
      #app-root {
        display: flex;
        flex: 1 1 100%;
        height: 100vh;
      }
    </style>
  </head>
  <body>
    <div id="app-root"></div>
  </body>
</html>

App.web.tsx

import React, {useState} from 'react';
import {View, Text, TouchableOpacity, StyleSheet} from 'react-native';

const App = () => {
  const [count, setCount] = useState(0);
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Hello from {'n'}React Native Web!</Text>
      <TouchableOpacity
        onPress={() => setCount(count + 1)}
        style={styles.button}>
        <Text>Click me!</Text>
      </TouchableOpacity>

      <Text>You clicked {count} times!</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#C3E8BD',
    paddingTop: 40,
    paddingHorizontal: 10,
  },
  button: {
    backgroundColor: '#ADBDFF',
    padding: 5,
    marginVertical: 20,
    alignSelf: 'flex-start',
  },
  title: {
    fontSize: 40,
  },
});

export default App;

index.web.js

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'),
});

webpack.config.js

const path = require('path');

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const appDirectory = path.resolve(__dirname);
const {presets} = require(`${appDirectory}/babel.config.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.web.tsx'), // Change this to your main App file
    path.resolve(__dirname, 'src'),
    ...compileNodeModules,
  ],
  use: {
    loader: 'babel-loader',
    options: {
      cacheDirectory: true,
      presets,
      plugins: ['react-native-web'],
    },
  },
};

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

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

module.exports = {
  entry: {
    app: path.join(__dirname, 'index.web.js'),
  },
  output: {
    path: path.resolve(appDirectory, 'dist'),
    publicPath: '/',
    filename: 'rnw_blogpost.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),
    }),
  ],
};

Package json file

  "dependencies": {
    "@react-native-community/async-storage": "1.12.1",
    "@react-native-community/picker": "1.8.1",
    "@react-native-firebase/app": "12.1.0",
    "@react-native-firebase/auth": "12.1.0",
    "@react-native-firebase/messaging": "12.1.0",
    "@react-native-masked-view/masked-view": "^0.2.6",
    "@stripe/stripe-react-native": "^0.2.2",
    "axios": "0.21.1",
    "babel-preset-es2015": "^6.24.1",
    "immutable": "4.0.0-rc.12",
    "intl": "1.2.5",
    "jwt-decode": "3.1.2",
    "mask-email-phone": "1.0.2",
    "moment": "2.29.1",
    "react": "16.13.1",
    "react-dom": "^18.2.0",
    "react-hook-form": "7.8.1",
    "react-native": "0.63.4",
    "react-native-actions-sheet": "0.5.4",
    "react-native-alert-notification": "^0.1.10",
    "react-native-animatable": "1.3.3",
    "react-native-calendars": "1.1263.0",
    "react-native-circle-floatmenu": "0.1.1",
    "react-native-circular-action-menu": "0.5.0",
    "react-native-config": "1.4.2",
    "react-native-confirmation-code-field": "7.1.0",
    "react-native-countdown-component": "2.7.1",
    "react-native-country-picker-modal": "2.0.0",
    "react-native-credit-card-display": "0.3.6",
    "react-native-device-info": "8.3.3",
    "react-native-elements": "^3.4.2",
    "react-native-fast-image": "8.3.7",
    "react-native-file-viewer": "2.1.4",
    "react-native-flash-message": "0.1.23",
    "react-native-gesture-handler": "1.4.1",
    "react-native-get-random-values": "1.7.0",
    "react-native-image-crop-picker": "^0.36.4",
    "react-native-image-picker": "4.0.4",
    "react-native-intl": "1.0.0",
    "react-native-linear-gradient": "2.5.6",
    "react-native-modal": "12.0.2",
    "react-native-modal-dropdown": "git+https://github.com/siemiatj/react-native-modal-dropdown.git",
    "react-native-navigation": "^7.22.1",
    "react-native-navigation-hooks": "6.3.0",
    "react-native-notifications": "4.0.0",
    "react-native-numeric-input": "1.9.0",
    "react-native-otp-verify": "1.0.4",
    "react-native-pager-view": "^5.4.6",
    "react-native-reanimated": "1.13.3",
    "react-native-responsive-fontsize": "^0.5.1",
    "react-native-responsive-screen": "^1.4.2",
    "react-native-safe-area-context": "3.2.0",
    "react-native-snap-carousel": "3.9.1",
    "react-native-spinkit": "1.5.1",
    "react-native-splash-screen": "3.2.0",
    "react-native-star-rating": "1.1.0",
    "react-native-step-indicator": "1.0.3",
    "react-native-super-grid": "^4.1.3",
    "react-native-svg": "^12.1.1",
    "react-native-tab-view": "^3.1.1",
    "react-native-unordered-list": "^1.0.4",
    "react-native-vector-icons": "8.1.0",
    "react-native-version-check": "3.4.2",
    "react-native-view-shot": "3.1.2",
    "react-native-web": "^0.18.9",
    "react-native-webview": "11.6.5",
    "react-redux": "7.2.4",
    "react-scripts": "^5.0.1",
    "redux": "4.1.0",
    "redux-define": "1.1.1",
    "redux-persist": "6.0.0",
    "redux-saga": "1.1.3",
    "rn-credit-card": "1.0.2",
    "rn-placeholder": "3.0.3",
    "uuid": "8.3.2"
  },
  "devDependencies": {
    "@babel/core": "7.14.3",
    "@babel/runtime": "7.14.0",
    "@react-native-community/eslint-config": "2.0.0",
    "@types/jest": "^29.0.3",
    "@types/react": "^18.0.20",
    "@types/react-native": "^0.70.2",
    "@types/react-test-renderer": "^18.0.0",
    "babel-jest": "27.0.2",
    "babel-loader": "^8.2.5",
    "babel-plugin-react-native-web": "^0.18.9",
    "eslint": "7.28.0",
    "html-webpack-plugin": "^5.5.0",
    "jest": "27.0.4",
    "metro-react-native-babel-preset": "0.66.0",
    "react-test-renderer": "16.13.1",
    "typescript": "^4.8.3",
    "url-loader": "^4.1.1",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.11.1"
  },

And this is the result of npm run web

Console after npm run web

Web preview (Only blank page)

Error Update

I found there is an error occured in web console when running the web.
Error logs

Then after searching for solution, i change some code on index.web.js.

index.web.js

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
import React from 'react';
import ReactDOM from "react-dom/client";

ReactDOM.createRoot(document.getElementById('app-root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// if (module.hot) {
//   module.hot.accept();
// }
// AppRegistry.registerComponent(appName, () => App);
// AppRegistry.runApplication(appName, {
//   initialProps: {},
//   rootTag: document.getElementById('app-root'),
// });

Then the errors become less. Only this one left.

Uncaught TypeError: Cannot read properties of undefined (reading '_updatedFibers')
    at requestUpdateLane (react-dom.development.js:25411:23)
    at updateContainer (react-dom.development.js:28810:14)
    at ReactDOMHydrationRoot.render.ReactDOMRoot.render (react-dom.development.js:29309:3)
    at eval (index.web.js:1:879)
    at ./index.web.js (rnw_blogpost.bundle.js:109:1)
    at __webpack_require__ (rnw_blogpost.bundle.js:1447:33)
    at rnw_blogpost.bundle.js:2493:37
    at rnw_blogpost.bundle.js:2495:12

2

Answers


  1. Chosen as BEST ANSWER

    I found the solution. After i read this post, your react, react-dom, and react-test-renderer must have same version. Then i downgrade my react-dom to version 16(which currently version 18, while react and react-test-renderer version 16).

    Then i change my index.web.js back to this.

    import {AppRegistry} from 'react-native';
    import App from '/App.web.tsx';
    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'),
    });

    And now it's working! enter image description here


  2. Change entry to ["babel-polyfill", path.join(__dirname, "index.web.js")]
    And publicPath in output to process.env.ROUTE_PREFIX || ""

    i,e

    entry: ["babel-polyfill", path.join(__dirname, "index.web.js")]
    output: {
        path: path.resolve(appDirectory, 'dist'),
        publicPath: process.env.ROUTE_PREFIX || "",
        filename: 'rnw_blogpost.bundle.js',
      }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search