But why? Can’t we use CRA or other similar tools.
While CRA can be a useful tool for quickly getting started with a new React project, creating a project from scratch has several advantages that can be valuable for developers who want more control, a deeper understanding of their project, or the opportunity to try out new technologies and approaches.
Creating a React project from scratch has several advantages over using a tool like Create React App (CRA).
One of the main advantages is that it gives you more control over the project setup and configuration. When you use CRA, the tool handles a lot of the setup and configuration for you, which can be convenient. However, this also means that you have less control over how the project is set up and how it is built.
On the other hand, when you create a project from scratch, you have the opportunity to fully customize the project setup and build process to suit your needs. This can be particularly useful if you have specific requirements or preferences for how your project should be set up, or if you want to use a particular build tool or configuration that is not supported by CRA.
Another advantage of creating a project from scratch is that it can help you better understand the underlying technologies and tools that are being used in your project. By setting up the project yourself, you will gain a deeper understanding of how the different pieces fit together and how they work, which can be valuable knowledge to have as you develop and maintain your project.
Finally, creating a project from scratch also gives you the opportunity to learn and experiment with new technologies and approaches. While CRA is a convenient tool that can help you get started quickly, it may not always support the latest technologies or best practices. By creating a project from scratch, you can try out new tools and approaches and see how they work in your project.
Before we start
Before starting, you need to set up the environment:
Installing Node.js
Node.js is an open-source, cross-platform runtime environment that helps us write JavaScript applications and execute them.
Node >=18.0.0 You can get it from: https://nodejs.org/en/. This will also install the npm (node package manager).
IDE
To start coding with React, we can use the Visual Studio Code IDE which can be downloaded from: https://code.visualstudio.com/download. I personally prefer vscode but you can use any text editor of your choice like atom, webstorm, sublime.
Let’s Start
To generate a React project without using any tooling, you can follow these steps:
-
Create a new directory for your project and navigate to it.
-
Initialize a new npm package by running
npm init
and following the prompts. This will create a package.json file in your project directory. -
Install the React and ReactDOM packages by running npm install react react-dom. This will add the React and ReactDOM packages to your project as dependencies and update the package.json file.
-
Create an
index.html
file in your project directory and add the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React CDN</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="index.js"></script>
</body>
</html>
- Create an
index.js
file in your project directory and add the following content:
import "/node_modules/react/umd/react.development.js"
import "/node_modules/react-dom/umd/react-dom.development.js"
const element = React.createElement("h1", null, "Hello world!")
ReactDOM.render(element, document.getElementById("root"))
-
Run a local development server by installing a tool such as http-server and running http-server in your project directory.
-
Open your browser and navigate to the URL of your local development server to view your React app.
Now let’s enhance this by using babel
to transpile ths .jsx
syntax and webpack
to generate dev and prod bundles.
To enhance the project that using webpack and Babel, we can follow these steps:
-
Install the webpack and webpack-cli packages by running
npm install --save-dev webpack webpack-cli
. This will add the webpack and webpack-cli packages to your project as development dependencies and update the package.json file. -
Install the Babel packages by running
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader
. This will add the Babel packages to your project as development dependencies and update the package.json file. -
Create a
webpack.config.js
file in your project directory and add the following content:
module.exports = {
entry: "./index.js",
output: {
filename: "bundle.js",
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
},
},
},
],
},
}
This configuration tells webpack to use the babel-loader to transpile your JavaScript code using the Babel presets for environment and React.
- Modify the index.html file to include the bundle file generated by webpack:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React CDN</title>
</head>
<body>
<div id="root"></div>
<!-- Webpack will create build at this path -->
<script src="dist/bundle.js"></script>
</body>
</html>
- Modify the
package.json
file to include the following scripts:
"scripts": {
"build": "webpack --mode production",
}
- Modify
index.js
to:
import ReactDOM from "react-dom/client"
const element = <h1>Hello World</h1>
ReactDOM.createRoot(document.getElementById("root")).render(element) // React 18
This script allow you to build your project for production using npm run build.
- Run
npm run build
to build your project. This will generatedist
folder withbundle.js
file. - Now run
index.html
usinghttp-server
module or you can uselive server
extension if you are using vscode. - Let’s include html template file in webpack configuration to generate a complete prod ready bundle.
To include the HTML template in webpack configuration, you will need to use a webpack plugin called html-webpack-plugin
. This plugin allows you to specify an HTML template file that will be used to generate an HTML file for your application.
To use the html-webpack-plugin
, you will first need to install it as a dependency for your project. You can do this by running the following command in your terminal:
npm install html-webpack-plugin --save-dev
Once the html-webpack-plugin is installed, you can add it to your webpack configuration by adding the following code to your webpack.config.js file:
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
// other webpack configuration goes here
plugins: [
new HtmlWebpackPlugin({
template: "path/to/template.html",
filename: "index.html",
}),
],
}
This will tell webpack to use the specified HTML template file to generate an HTML file for your application. The generated HTML file will be placed in the output directory specified in your webpack configuration and will be served when your application is built and run.
You can also use the html-webpack-plugin to customize the generated HTML file by specifying various options such as the title of the page, the favicon, and any metadata that should be included in the head of the HTML file. For more information on the options available for the html-webpack-plugin, you can refer to the plugin’s documentation.
- Run
npm run build
to build your project. This will generatedist
folder withbundle.js
andindex.html
file. - You can run this
index.html
similarly usinghttp-server
module or you can uselive server
extension. - There’s something still pending in this config which is we can not import css files to project.
- To setup this install style loader and css loader by running
npm install --save-dev style-loader css-loader
. - Now add this to webpack config by making following changes.
// Add this to rules array in module object.
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
}
- Create a
style.css
file and add some styles in it. - Import this file in
index.js
- Repeat step 8 & 9. You will see the css changes applied. But this will add changes in js bundle and webpack will inject these as style tag on run time.
- To extract css into separate file we can use MiniCssExtractPlugin.
But it’s not practical to build the project after making a single change. So we will setup a dev mode using webpack-dev-server
- Install Webpack Dev Server using
npm install --save-dev webpack-dev-server
. - Create a
webpack.config.dev.js
and add following code.
const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
entry: "./index.js",
output: {
filename: "bundle.js",
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
},
},
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
],
devServer: {
historyApiFallback: true,
},
devtool: "source-map",
}
-
We have some extra properties in this config.
historyApiFallback
: The development server will use the historyApiFallback option to support client-side routing.source-map
: The source-map option will generate source maps for the output bundle to facilitate debugging.
-
This configuration file can be used with the
webpack-dev-server
to start a development server for project. To use this add following script topackage.json
."start": "webpack-dev-server --mode development --config webpack.config.dev.js"
-
Run
npm run start
ornpm start
to start the development server and view your React app. Webpack will automatically transpile your code using Babel and reload the page when you make changes.
Now we can change the folder structure a bit so that we can make it similar to conventional react project.
-
Create a folder with name
public
at root and moveindex.html
to this folder. -
Create a folder with name
src
at root and moveindex.js
andstyles.css
to this folder. -
Also we need to change these paths webpack and webpack dev config.
entry: "./src/index.js",
new HtmlWebpackPlugin({ template: "public/index.html", }),
After this activity your folder structure should look like:
Now we need to have a way to copy static assets like fonts and images to the bundle folder
-
To copy static assets such as images, fonts, or other media files using webpack, you can use the
copy-webpack-plugin
and thefile-loader
.npm install --save-dev copy-webpack-plugin
Here is an example of how to use the copy-webpack-plugin to copy static assets in your webpack configuration file:
const CopyWebpackPlugin = require("copy-webpack-plugin")
module.exports = {
// your webpack configuration goes here
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: "public",
to: "assets", // This will be the path you will use to import the file. For eg. /assets/image.png
globOptions: {
ignore: ["**/index.html"], //Ignore index.html file
},
},
],
}),
],
}
This configuration will copy all files from the public
directory to the assets directory in the output directory.
Now you can import the files present in public folder using absolute path directly in image src or in css url
- Copy an
image.png
file topublic folder
- Change element variable in
index.js
to
const element = (
<>
<h1>Hello World</h1>
{/* Src path will be the path you will give in `to` property in config */}
<img src="/assets/image.png" />
</>
)
- Restart the server and you will see and image in browser.
copy-webpack-plugin is not designed to copy files generated from the build process; rather, it is to copy files that already exist in the source tree, as part of the build process.
This means that if we want to import images dynamically into .js
or .css
we can not use this.
For that we need to use file-loader
npm install --save-dev file-loader
Copy following code to webpack.config.dev.js
rules array.
{
test: /\.(png|jpg|gif|svg)$/,
use: {
loader: "file-loader",
options: {
name: "[name].[ext]",
outputPath: "assets/",
},
},
}
Now you can import image file to js file using import syntax. Let’s try it out
- Copy a image file
image2.png
to src folder. - Import file in
index.js
using following syntax.
import image from "./image.png"
- Modify
img
tag to use the imported file
const element = (
<>
<h1>Hello World</h1>
<img src={image2} />
</>
)
Now you can see the result in browser.
To get a bit clarity how this works add a new image tag with src path from assets and run npm run build
.
Check the dist folder to how webpack is generating the final build.
At last
Now as a final step we can modify webpack.config.js
a bit to include versioning in the build process.
Webpack provides versioning support out of the box.
In webpack.config.js
modify output.filename
to bundle.[hash].js
.
Delete the dist folder and run npm run build
.
Webpack will generate bundle with a hash code.
If you are pushing the code to git add .gitignore
file at root level and add following code
node_modules
dist
This will ignore the node_modules and dist folder as we don’t need to push these.
What Next
- We can add
sass
orpostcss
support using loaders. - Extract out css to separate bundles and hash css bundles also.
- Add svgr to import svg as react components.