I am trying to create a React Native Web project.
I have built several React Native apps before, but have never tried to put one on the web.
My biggest problem has been incompatibility between native libraries when launching the web - not an unexpected problem.
Anyway, my goal is to be able to load native libraries when on a native platform and having alternative libraries doing the same thing when on the web.
For example, I am getting the current error:
./node_modules/react-native-calendars/src/expandableCalendar/asCalendarConsumer.js
Module parse failed: Unexpected token (11:8)
You may need an appropriate loader to handle this file type.
| render() {
| return (
| <CalendarContext.Consumer>
| {(context) => (
| <WrappedComponent
How would I fix this? This library is theoretically compatible with React Native Web, and yet I get the above error.
Would this loader be in Babel? Metro? Webpack?
I have a babel.config.js that looks like this:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
resolve: {
alias: {
'react-native$': 'react-native-web'
}
},
rules: [
{
test: /\.(js|jsx|mjs)$/,
include: [
paths.src,
// In order to use react-native targetted libraries on web,
// we have to use babel to compile them from ES6 to ES5.
// This would still not allow us to use libraries that have RN
// dependencies that are not polyfilled by react-native-web.
path.resolve(paths.nodeModules, 'react-native-vector-icons'),
],
loader: 'babel-loader',
options: {
compact: true,
presets: ['react-native'],
},
}
]
};
I have a metro that looks like this:
const { getDefaultConfig } = require("metro-config");
module.exports = (async () => {
const {
resolver: { sourceExts }
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve("react-native-css-transformer")
},
resolver: {
sourceExts: [...sourceExts, "css"]
}
};
})();
And here is my webpack:
// webpack.config.js
module.exports = {
plugins: ["@babel/plugin-syntax-dynamic-import"],
resolve: {
alias: {
'react-native$': 'react-native-web'
},
},
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
//exclude: /node_modules/,
options: {
presets: ['es2015', 'stage-0', 'react', 'babel-preset-env', 'babel-preset-stage-0'],
plugins: ["@babel/plugin-syntax-dynamic-import"],
}
},
{
test: /\.ttf$/,
loader: "url-loader", // or directly file-loader
include: path.resolve(__dirname, "node_modules/react-native-vector-icons"),
},
]
}
I'm really quite lost on how to setup a Webpack, or how I am supposed to be using these files to get rid of the above error.
Where do I add the loader the error is asking about?
Sorry if this is a confusing question - this part of RN is completely new to me
Actually, I faced an issue like it but not the web, our project needs to have a unique logic but different UIs for Android and iOS, so we decide to decoupled the UIs with different files by .android.js
and .ios.js
files, its name is Platform-specific extensions and also config it on the .babelrc
file:
{
"presets": ["module:metro-react-native-babel-preset", "module:react-native-dotenv"],
"plugins": [
"lodash",
["module-resolver", {
"extensions": [".android.js", ".ios.js", ".js"], // here
"cwd": "babelrc",
"root": ["./app"]
}]
],
"env":{
"production":{
"plugins": ["transform-remove-console"]
}
}
}
So for decoupling the UI for each stack be like below:
CheckoutPage.android.js
CheckoutPage.ios.js
For importing the component we use this way:
import Checkout from '[pathToComponent]/CheckoutPage';
~~~
<CheckoutPage ...
Now my suggestion is using another file extension, the web.js
and put it in the babel configuration:
"extensions": [".android.js", ".ios.js", ".web.js", ".js"],
Furthermore, add the web.js
extension to the Webpack configuration for loading in the web build and use ignore-loader
to ignore .ios.js
and .android.js
files on the web build:
// webpack configuration file
module.exports = {
module: {
rules: [
{
test: /\.(js|web.js)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
{
test: /\.(android.js|ios.js)$/,
exclude: /node_modules/,
use: {
loader: "ignore-loader",
},
},
],
},
~~~
resolve: {
extensions: ['.web.js', '.js'],
},
};
For a better explanation, I create a project on CodeSandBox, and you can see there I just call import Home from './Home';
but the Home.web.js
component is rendered.
By using this trick you can use platform-specific extensions
even on the web build or develop.