Creating A React App Without create-react-app
In this article, we will create a React app from scratch without create-react-app library in order to understand how it works in essence. We will use Webpack as the module-bundler for this project. I will explain everything at the time I implement it in the project.
Before we start, clone the github repo that I created, which contains the src folder with minimum starter files. Once done, open the folder in your code editor, and make sure you're in starter-files branch.
Link to this heading1. Initialize NPM
Run npm init -y
in the root of the project folder. It should create a package.json file that looks like this-
C:\Users\sapin\Desktop\react-webpack-starter>npm init -y
Wrote to C:\Users\sapin\Desktop\react-webpack-starter\package.json:{
“name”: “react-webpack-starter”,
“version”: “1.0.0”,
“description”: “”,
“main”: “index.js”,
“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1"
},
“keywords”: [],
“author”: “”,
“license”: “ISC”
}
Link to this headingThe Need For Webpack
The very first thing we need is not the react library, but a module-bundler that will scan through our project files and bundle them together. But before bundling the code together, Webpack uses the tools we provide to understand the stuff present inside the files. Don't worry! We'll discuss it later in details.
Webpack will start its job by picking up src/index.js file, because this is what we specify as the entry-point when configuring it up. So let's jump right into it-
We need the following libraries from Webpack for anything to work with it-
-
webpack: It's the core library for Webpack bundler.
-
webpack-cli: If you take a look inside package.json when using create-react-app, you'll find the following scripts-
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
}
react-scripts is a custom library that uses webpack's command line interface to run any command,
such as- npm start
. But in this setup, we're going to run commands from raw webpack-cli.
"scripts": {
"start": "webpack-dev-server --mode development",
"build": "webpack --mode production",
}
- webpack-dev-server: You already saw the use of this library in the
start
script above. It is used in development mode to serve the bundled files in a browser; e.g. localhost:3000.
Link to this heading2. Setting Up Webpack
First, run the following command in the console. The --save-dev
or -D
flag states that we want to install them as dev-dependencies.
npm install --save-dev webpack webpack-cli webpack-dev-server
Now add the above-mentioned scripts inside package.json file.
"scripts": {
"start": "webpack-dev-server --mode development --open --hot",
"build": "webpack --mode production",
}
The two flags --open
and --hot
serve the following roles respectively:
- automatically open the app in browser after the server starts
- automatically reload the app in browser whenever there's a change in the server code
So far, we have the following stuff in our project folder-
Now let's configure webpack by creating a file webpack.config.js at the root of the folder. Add the following stuff into it-
module.exports = {
entry: {
index: './src/index.js',
},
output: {
path: __dirname + '/build',
filename: 'index.bundle.js',
},
};
Everything inside module.exports
is exported as configuration for webpack.
-
entry: Here we specify the file(s) that webpack should look for to start the bundling process.
-
output: The directory where the bundled file(s) will be placed. In our case, we have just a single entry-point named
index
, and so the output should generate a single bundled file only. Let's see its properties-- path: Path to the output directory.
__dirname
, which is provided by node, holds the path of current working directory; i.e. the root of our project folder. Thus,__dirname
+'/build'
evaluates to /build folder in the root. - filename: Name of the bundled file. (index.bundle.js)
- path: Path to the output directory.
Now install html-webpack-plugin
, which will pick up src/index.html, and create an html file inside /build folder, placing the same
content as in src/index.html.
npm install --save-dev html-webpack-plugin
Update webpack.config.js with the following content-
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/index.js'
},
output: {
path: __dirname + '/build',
filename: 'index.bundle.js'
},
plugins: [
new HTMLWebpackPlugin({
template: './src/index.html'
})
]
}
Link to this headingAll it does is-
- require the
html-webpack-plugin
- add, to
plugins
array, a new plugin namedHTMLWebpackPlugin
, and pass to it the source file that will be used as a template to create new html file in the /build directory.
And now we're ready to tackle Babel and React/JSX.
Link to this headingIntroducing Loaders
The fact of the matter is that excluding html & js file-types, Webpack actually knows nothing about what files there are in /src folder, and what those files do.
And here comes the role of a loader. A loader acts as a pre-processor for specific file-types. When Webpack encounters any new file-type, it looks for a loader in its configuration, and handles the file to that loader. The loader configured for that file-type would perform its magic, and return the transpiled version of that code to Webpack which then continues the bundling process.
Link to this headingThe Need for Babel
Webpack doesn't understand the modern JavaScript code that we're used to write. It only understands code prior to ES2015. But we can use babel for this job. Babel is a JavaScript compiler that transforms modern JavaScript code into backward compatible version of it. It can also transform JSX and React. And that's what we actually need here! 😄
Here are the dependencies/libraries that we need to implement babel-
- @babel/core: It's the core library for Babel.
- @babel/preset-env: This preset is a collection of babel plugins to transform modern JavaScript code, depending on the environment/target-browser we specify in the configuration.
- @babel/preset-react: This preset is the one that transforms JSX syntax into actual JavaScript code we need.
- babel-loader: This one should've been mentioned before rest of the dependencies above. If we want to use any of the three things above, we must have babel-loader so that it can pre-process the specific file-types (in this case, js/jsx) before Webpack can work further.
Link to this heading3. Setting Up Babel
Run the following command with --save-dev
flag as earlier-
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/preset-react
Add module
property to module.exports
object in webpack.config.js-
module: {
rules: [{
test: /\.js|\.jsx$/,
exclude: /node_modules/,
use: 'babel-loader'
}]
},
Each object in rules
array specifies rules for different types of files. Let's inspect the rule properties-
- test: It takes a regular expression for the file-type(s) to look for.
- exclude: As you may guess, it restricts the loader for corresponding file-types to not enter specified directory/directories.
- use: This property tells Webpack to use babel-loader for file-types mentioned above.
Link to this headingAll You Should Understand So Far-
- Webpack looks for the entry-point (/src/index.js) when we hit
npm run start
(npm start
for short). - When it encounters a file with js/jsx extension, it looks for any corresponding loader in its configuration. If it finds one, it handles the file to that loader; else throws an error.
After Webpack will hand index.js over to babel-loader, the loader will look for its own configuration. So let's have one!
Create a new .babelrc file in the root directory, and add the presets
that we're using for babel-
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
Now that our Webpack settings are able to interpret and transform JSX code, let's actually install react and react-dom libraries!
npm install react react-dom
Notice that I didn't use --save-dev
because these dependencies are used in both development and production modes.
Now you can import
react and react-dom in your project files, and start using JSX!
And if you run npm start
, it should start webpack-dev-server, and automatically open the app in your browser after Webpack completes
the bundling process.
Link to this headingBINGO!!! But Wait…
Despite all of the stuff we've done, we are still limited to not using any file-types other than js/jsx in our project because we have to specify loader(s) for each file to tell Webpack what to do with it.
Some commonly used loaders are: css-loader, sass-loader, url-loader, file-loader, etc. You should know how to use some of the loaders listed here.
In the complete
branch of my repository, I have added support for a lot more things like sass, eslint, image-files,
postcss-loader with autoprefixer, file-loader, url-loader, and many more.
By now, you should have a pretty good understanding of how React and Webpack work together.
And, if you like my writing style, you can subscribe to my mailing list in order to never miss any of my future posts!