In our pursuit of creating the ultimate UI development starting point, it is time to take the first step with react-native-web
This is the second part of a series where I go through all the steps of creating The Ultimate React Native UI Library starter repo. Please visit the first post (just linked 👆🏻) for a general description of what we are trying to accomplish and where all the Steps are numbered + linked.
The finished source code can be found here react-native-storybook-boilerplate
The other parts
Article | Link |
---|---|
setup react native & @storybook/react-native | Step 1: Setting up React Native with Storybook |
setup react from scratch together with react native web | You are here now! |
setup @storybook/react + react native web to run as a parallel storybook | Step 3: Setting up storybook with react native web: Show your mobile components in a browser |
What's covered in this post? ->
- setting up react from scratch
- installation
- webpack
- babel
- setting up react-native-web
- installation
- webpack alias configuration
Please note that this is not a webpack or babel tutorial so I will more or less not cover the basics of those
If you want to go really in depth in how to set up React from scratch I really recommend a tutorial series from codecademy:
- React Setup Part 1: React and ReactDOM
- React Setup Part 2: Babel
- React Setup Part 3: Webpack
- React Setup Part 4: HTMLWebpackPlugin
- React Setup Part 5: Conclusion
I thought that series was very good.
Setting up React - Installing our dependencies
If you are following along from the first part in the series you have a "normal" react-native
and storbook
already set up and running locally, and now it's time to add React
into the mix.
installation
Obviously we need react, but it comes with the react-native installation but we need to add react-dom
->
yarn add react-dom
and then we need to install our babel dependencies babel
yarn add --dev @babel/core babel-loader @babel/preset-react @babel/preset-env
Then we also need to ochestrate the packaging so let's install webpack also while we are at it:
yarn add --dev webpack webpack-cli webpack-dev-server html-webpack-plugin
Add scripts to package.json
You can do this in any order you like but, I for some reason, like to add scripts first. I think it gives me a sense of what I'm trying to acomplish.
"scripts": {
...
"build-react": "webpack --mode production",
"start-react": "webpack-dev-server --config ./webpack.config.js --mode development",
...
}
You obviously can name the scripts anything you like & makes sense to you: I named my scripts start-react
& build-react
.
Configure Webpack
This is where the magic happens 🤹🏻♀️ In the root folder add a webpack.config.js
file and add the folowing:
const path = require('path')
const HTMLWebpackPlugin = require('html-webpack-plugin')
const HTMLWebpackPluginConfig = new HTMLWebpackPlugin({
template: path.resolve(__dirname, './public/index.html'),
filename: 'index.html',
inject: 'body',
})
module.exports = {
entry: path.join(__dirname, 'index.web.js'),
output: {
filename: 'bundle.js',
path: path.join(__dirname, '/build'),
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules\/(?!()\/).*/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
],
},
plugins: [HTMLWebpackPluginConfig],
devServer: {
historyApiFallback: true,
contentBase: './',
hot: true,
},
}
for a better description of what's going on I really recommend this article from codecademy
React Setup, Part III: Webpack
Here's a rough description:
entry | what it does |
---|---|
entry | Tells Webpack the root file of our application. Starting from that file it will go through the whole tree and transform all code which match our webpack rules. I've named this file index.web.js we need to remember to create this file. |
output | Configuration for the output files from react. filename gives the packed javascript a name. path sets an output folder for the packed files |
rules |
test is a regular expression which matches to our source files, i.e. *.js . exclude excludes files we don't want webpack to touch. use this is where we plug in babel i.e. the stuff that will transform our react code into vanilla js. |
After webpack are dont with the JS it needs to make a new HTML file as well, that's where HTMLWebpackPluginConfig
comes in, please refer to this article for a better description: React Setup, Part IV: HTMLWebpackPlugin.
Let's take a look at the code for the HTMLWebpackPlugin
closer:
const HTMLWebpackPluginConfig = new HTMLWebpackPlugin({
template: path.resolve(__dirname, './public/index.html'),
filename: 'index.html',
inject: 'body',
})
-
template
: It tells our plugin what template file it should use and copy to our./build
folder. I set it to a file in the folderpublic
and the file name isindex.html
. (We shall not forget to create these.) -
filename
: Is the name of the newly created file which it copies. As I mentioned above this file will wind up in./build
folder. -
inject
: Is where the our JavaScript script tag will be injected. Bothhead
andbody
are valid options.
What's the path
stuff?
It's just a way to concatenate path-strings instead of using a +
sign, __dirname
meaning the current directory which the file is in.
Add entry files
Add public index.html
As I've configured the HTMLWebpackPlugin
, and shortly explained right above, we need to add a index.html
into a folder called public
,
So! In root
create a folder called public
and inside of that folder create a file called index.html
and the following html
<!DOCTYPE html>
<html>
<head>
<title>React Native Web Storybook</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
Take notice of the id
-name (app
) of the div where we are injecting the react single page application. All of these files are react
boilerplate which are basically the same when using the library.
Add index.web.js
In the root add index.web.js
and code the following:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App.web'
ReactDOM.render(<App />, document.getElementById('app'))
this is where <div id="app"></div>
needs to match document.getElementById('app')
If you've used create-react-app
before you'll see that it's basically the same code as they generate for you, in our case I wan't to keep web react
seperate from react-native
so I named the file with the extension .web.js
.
From the code we also see that we need to add our <App />
component, so let's do that next:
Add App.web.js
In the root add App.web.js
, this will be the entry component for react, and add the following code:
import React from 'react'
function App() {
return (
<>
<h1>Hello world from react</h1>
</>
)
}
export default App
As you can see this is normal jsx, we will come to adding react-native-web
after we confirm that our vanilla react
setup works first.
Test our React configuration
It's time to check if we are able to run everything together so let's run our script:
yarn start-react
Hopefully this starts the Webpack development server for you and you see this page:
Great Success Time to add React-Native-Web into the configuration!
Install React-Native-Web
For those who are not quite familiar react-native-web
makes it possible to use the react-native-api
to write components for the web. It transforms View
to div
etc. so it's readable by a web browser. Really cool stuff!
"React Native for Web" makes it possible to run React Native components and APIs on the web using React DOM.
It's open source and do check it out!
react-native-web
Installation
yarn add react-native-web
When that's done we need to modify our webpack
configuration so it swaps out all our react-native
imports for react-native-web
.
Re-configure webpack.config.js
so it our awesome webpack file let's add the following lines:
module.exports = {
...
resolve: {
alias: {
'react-native$': 'react-native-web',
},
},
...
}
That's enough configuration for now!
Let's modify our App.web.js
to use the react-native-api
.
import React from 'react';
import {View, Text} from 'react-native';
function App() {
return (
<View>
<Text>Hello world from react native</Text>
</View>
);
}
export default App;
run yet again:
yarn start-react
and BEHOLD!
With this we can now use the whole react-native
-api for the web, you can have a look here: React-Native: Core Components and APIs
To extra check this we can, for instance, add an <ActivityIndicator />
component to our App.web.js
import React from 'react';
import {View, Text, ActivityIndicator} from 'react-native';
function App() {
return (
<View>
<Text>Hello world from react native</Text>
<ActivityIndicator />
</View>
);
}
export default App;
And here's the result!
That's it for this part in the series!
Thanks for reading and again, you can find the finished project here: react-native-storybook-boilerplate
Top comments (7)
Hey @Carl-W - thanks for this write-up. If you want to add a small update, in the Configure Webpack section, 'contentBase' in the dev server configuration should now be 'static': github.com/webpack/webpack-dev-ser...
Great heads up ! 🚀
Great tutorial!
One issue I ran into in this article is that the default webpack-cli installed by yarn is not compatible with webpack-dev-server. Changing
webpack-dev-server
towebpack serve
in thestart-react
script of package.json seems to fix it.Thank you for this blog, its excellent.
Have you managed to get react-navigation working in such an app. I followed this blog and I can run the app on both Android and Web. As soon as i add in react-navigation it all breaks and the web does not run. I get the following error
Swipeable.js:1 Uncaught Error: Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: C:\0_LEARNING\Headless_Apps\ReactNativeWeb\plainApp\plainapp\node_modules\react-native-gesture-handler\Swipeable.js: Missing class properties transform.
if i add the following in the webpack.config.js under options
plugins: ['@babel/plugin-proposal-class-properties'],
I then get the following error
Directions.js:1 Uncaught TypeError: Cannot read property 'Direction' of undefined
at eval (Directions.js:1)
at Object../node_modules/react-native-gesture-handler/Directions.js
Have you got any idea how to resolve this?
Awesome tutorial!
Just a headsup for those who get an "Invalid options object" error while running webpack-dev-server. The option "contentBase" in webpack-config.js has been renamed to "static", so you'll need to change that.
Happy coding <3
Awesome heads up for everyone using a newer version of webpack !
Thanks for reading 🚀
Great article! If you are looking to set up your React with React-Native-Web, then it is essential to get the right help. A React Native development company can help you navigate the complexities of setting up and maintain your React Native platform. This can be a great way to ensure that your React Native experience is as stress free as possible.