Creating and setting up your own React project from scratch can be, at times, a little bit tricky, even though it's not your first time starting a React project. That's why we have create-react-app, a command that prepares and installs all the boilerplate for us and have our application ready to rock from the very first moment after the process finishes.
But, even though create-react-app is a very good choice to start off with, specially for those who are new to the React world or who just don't want to spend time setting up everything, it's interesting to know that there's another way to get things done.
As you can imagine, this method is not going to be as easy as just running a command, but it will surely be way more satisfactory and useful for your developer career (not to mention that it's a reusable process and you'll get rid of not-that-necessary stuff that comes together with create-react-app and that will overload your project).
Note that I won't be using a specific structure for the project because I think that's very personal and every developer works in a different way, so, I leave that choice up to you 😉
So, with all this said, let's dive into it!
Step 1: Creating the folder that will host our project
We're going to start by creating a new directory for our project at a location of our choice.
mkdir my-react-project
Once created, we're going to navigate into it.
cd my-react-project
Step 2: Initializing the project
To initialize our project, we're going to run a npm command.
npm is a package, version and dependencies manager made for JavaScript. If you haven't installed npm yet, you need to directly install Node.js, since they work together and npm is included in Node.js installation as well. Node.js will let us execute JavaScript on server side.
You can perfectly use a different package manager, like Yarn or Bower.
If you're not sure if you have previously installed npm/ Node.js, just run the following commands to check the last versions installed for them. If these commands return a version number, then you already have them on your computer. Otherwise, you'll need to install them again.
npm -v
node -v
Once we have npm and Node.js ready to use on our computer, we're going to run the following command:
npm init
This command will create a package.json file, which is the file where all the dependencies and scripts for our project will be specified.
Throughout the process of the file creation, the terminal will pop up some questions to let you set up your project with proper information about it. If you want to skip the current question, just press enter to jump onto the next one.
If you don't feel like giving extra information for the project or just want to configure it later, just add the -y flag to the command:
npm init -y
Once the installation is done, we'll have a new package.json file in our project that will look like this:
{
"name": "my-react-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Step 3: Installing dependencies
We also need to install a few dependencies manually in order for our future React application to work properly and as expected.
React dependencies
We'll be installing the following dependencies on React's side:
react
: the JavaScript library we'll be working with.
react-dom
: package with some methods to manage DOM elements.
react-router-dom
: package that contains the DOM bindings for React Router.
npm install react react-dom react-router-dom
Webpack dependencies
We'll also need a module bundler to get our project ready for the web. Webpack bundles all the JavaScript files in your project and prepares all the necessary resources for usage in the browser.
As we only need Webpack to work in the development environment, we're going to install all its related dependencies adding the flag --save-dev or simply -D to the command.
We'll be installing the following dependencies on Webpack's side:
webpack
: the bundler.
webpack-cli
: CLI for Webpack.
npm install --save-dev webpack webpack-cli
Babel dependencies
Babel is a JavaScript compiler that converts (or transpiles) JavaScript ES6 to JavaScript ES5 since not all browsers currently support ECMAScript 6 features.
The Babel-related dependencies we're going to install are the following:
@babel/core
: Babel compiler core.
@babel/preset-react
: package that contains a set of plugins used to support React features.
npm install --save-dev @babel/core @babel/preset-react
Before going further on dependencies, we're going to configure Babel in our project in order for our JavaScript files to be transpiled as intended.
Let's go back to the terminal to create a new file for this configuration:
touch .babelrc
Then, just add the following code snippet:
.babelrc
{
"presets": [
"@babel/preset-react"
]
}
Loaders for Webpack
Webpack needs something called loaders to preprocess files. They are useful to bundle static resources beyond JavaScript.
These will be the basic loaders we'll be working with:
babel-loader
: loader for Babel.
html-loader
: loader for HTML.
style-loader
: loader that injects styles into the DOM.
css-loader
: loader for CSS.
sass-loader(*)
: loader for SASS/SCSS.
(*) This loader is not strictly necessary, but in case you want to use a CSS preprocessor, you'll need a loader for it as well. There also exists loaders for LESS, PostCSS, Stylus...
npm install --save-dev babel-loader html-loader style-loader css-loader sass-loader
Webpack plugins
We also need Webpack plugins that will help for bundling purposes.
html-webpack-plugin
: this plugin is used to create HTML files that will serve bundles.
Note: Since we're working in a development environment, we'll just be using a loader for styles, but for production builds, it' recommended to extract the CSS from the bundle using MiniCssExtractPlugin. This plugin extracts CSS into separate files and creates files for every JavaScript file which contains CSS.
The loader is faster and will set the styles as internal, inside the <style>
tag, while the plugin will link the external styles file to the HTML document using the <link>
tag.
Server dependencies
Our app will also need a server, so we'll be using server-related dependencies as well. We're going to install the following:
express
: Node.js framework that we'll be using to create our server file and to handle server requests.
nodemon
: tool that will refresh our web app whenever a change occurs in the app directory.
npm install express
npm install --save-dev nodemon
Step 4: Configuring Webpack
Next step is putting Webpack's loaders and plugins we just installed together in a config file to let it know how its behavior should be in the bundle process of our files.
To start with, we're going to create an empty file for this purpose. Back on the terminal:
touch webpack.config.js
Before diving into the content of this config file, we're going to see what it really does and how it behaves.
First off, we need to tell Webpack which the entry point for our app will be. This entry point will be a JavaScript file called index.js.
We also need to specify the output file, which will be the final JavaScript file all bundled and the only one that will be referenced explicitly from the HTML file served.
At this point, it's important to mention the dist folder. This folder is a directory created as part of the bundling process and will hold all the static files generated as a result of it.
More stuff Webpack needs to know is the type of files it'll be working with to translate them properly. For the moment, those types are JavaScript, HTML, CSS and SASS/SCSS. But, if in the future we need to work with more different kind of files (and we definitely will), such images, fonts, etc, these will need their own loaders as well.
And finally, we also need to configure the necessary plugins. In this case, HtmlWebpackPlugin, which will generate the HTML that will be served to the browser.
webpack.config.js
const path = require("path");
const webpack = require("webpack");
const HTMLWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./index.js",
output: {
filename: "bundle.js",
path: path.resolve("dist"),
publicPath: "/",
},
module: {
rules:[
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: "babel-loader"
},
{
test: /\.html$/,
use: "html-loader"
},
/*Choose only one of the following two: if you're using
plain CSS, use the first one, and if you're using a
preprocessor, in this case SASS, use the second one*/
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.scss$/,
use:[
"style-loader",
"css-loader",
"sass-loader"
],
},
],
},
plugins: [
new HTMLWebpackPlugin({
template: "index.html"
}),
]
}
Step 5: Creating the HTML template
Well, this is the simplest step but still we need to take it 🙂
We need to create a basic HTML document that will be used by HTMLWebpackPlugin as a template to generate the new one. As easy as that.
index.html
<!DOCTYPE html>
<html>
<head>
<title>My React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
Pay attention to the empty <div>
with id root. We'll learn about it later.
Step 6: Creating the server
The server is going to be a new JavaScript file that will open up a port to listen on, do a little bit more of configuration and make a few requests.
touch app.js
We're going to call it app.js but you can give it any name of your choice. Just be careful with capitalization. You'll understand why later.
app.js
const express = require("express");
const app = express();
const path = require("path");
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`The app server is running on port: ${port}`);
});
const DIST_DIR = path.join(__dirname, "dist");
const HTML_FILE = path.join(DIST_DIR, "index.html");
app.use(express.json());
app.use(express.static("public"));
app.use(express.static("dist"));
app.get("/", (req, res) => {
res.sendFile(HTML_FILE, function(err){
if(err){
res.status(500).send(err);
}
});
});
What we are doing in this file is starting a new server that listens on port 3000 for connections. Then, the HTML file generated by Webpack is sent to the root URL (in other words, this HTML will be the homepage of our app). We're also indicating that every file in the directories public and dist will be static and should be treated as such.
Step 7: Creating the React app
Now, we're going to create App.js, which will be the main component of our React app (capitalization alert here!).
App.js
import React from "react";
const App = () => {
return <div>Hello, World!</div>;
};
export default App;
The render of this component will be injected into the served HTML, so what we'll see when we open up the browser will be Hello, World!.
Let's take a look at how we can do this.
Step 8: Creating the entry point for the React app
In this step, we're going to specify the routing for our app and also, where in the DOM the content from React should be inserted.
index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import App from "./App.js";
import "./styles.scss";
const appRouting = (
<Router>
<Switch>
<Route exact path="/" component={App} />
</Switch>
</Router>
);
ReactDOM.render(appRouting, document.getElementById("root"));
We're just indicating that the App component should be rendered when the URL matches the root path exactly, and that the render content should be placed inside the tag with id root in the index document.
Step 9: Defining the scripts
And, finally, we're going to set up the scripts to be able to build and run our app.
Back in package.json, we initially had something like this in the scripts section:
{
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
}
...
}
Now, we're going to add a couple more: run and build, like this:
{
...
"test": "echo \"Error: no test specified\" && exit 1",
"build": "rm -rf dist && webpack --mode development",
"dev": "nodemon app.js"
...
}
Step 10: Building and running our app
Last step is (finally) building and running our app.
We first need to run a build command to bundle everything.
npm run build
And then, just run it.
npm run dev
Our app is now available at: localhost:3000
.
And... that's it! Now we have our React application ready to start working with it 🙌🏼
If you've reached this point, thank you very much. I hope that this tutorial has been helpful for you and I'll see you all in the next.
🎉 Don't forget to follow me on Instagram and Twitter for more related content.
Top comments (30)
You don't need an Express server though. Better use live-server, even better, use webpack-dev-server which not only run the app on localhost but also supports HMR for CSS, JS along with in-memory compilation for faster dev builds and has historyFallbackApi option. Express server is simply an overkill, not to mention the management overhead.
I was about to mention just this
Great article though @underscorecode
An why using --save-dev. Will it still work without using --save-dev?
Google it.
I got this error after following everything:
PS C:\Users\USER\projects\reactProj\react-manual> npm run build
'rm' is not recognized as an internal or external command,
operable program or batch file.
npm ERR! code 1
npm ERR! path C:\Users\USER\projects\reactProj\react-manual
npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c rm -rf dist && webpack --mode development
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\USER\AppData\Local\npm-cache_logs\2021-06-07T10_51_10_993Z-debug.log
PS C:\Users\USER\projects\reactProj\react-manual> npm run build
'rm' is not recognized as an internal or external command,
operable program or batch file.
npm ERR! code 1
npm ERR! path C:\Users\USER\projects\reactProj\react-manual
npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c rm -rf dist && webpack --mode development
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\USER\AppData\Local\npm-cache_logs\2021-06-07T10_51_29_766Z-debug.log
PS C:\Use
what does this mean?
Good post. Just a few questions from my side: for the webpack css loader you included solutions for plain css, and for sass & less preprocessors, but for use a css in js solution, what loader should I use? Also I will love to see a config optimized for production later maybe in another post. Thanks in advance.
I guess you mean PostCSS. There’s actually a Webpack loader for it as well, which is
postcss-loader
, and you can give it a basic configuration like the following:Note that you’ll also need to install
postcss-preset-env
.Happy coding!
Forgive my ignorance. I don't know if we are talking about the same so I'll explain. I mean all the CSS in JS libraries out there like styled-components, JSS and others. Those are the ones that use the PostCSS loader? Thanks in advance
I'm so glad I found this and a special thanks for including express I wanted to see how the integration works for so long.
You can also try parcel or esbuild to do that, better results with a lot less code.
Anyway post was worth reading.
I'm glad you found it useful.
showing the file structure would be really helpful. i try to make
App.js
but it wont let me because it shares a name withapp.js
even with the capitalization. does everything just go in the root? are there other folders?I wanted to know how to add React to my HTML.
It's something I wanna do to practice and learn REACT. Because my computer cannot load node at the moment but then I cannot wait for a new computer.
Any ideas ?
Nice and informative
Thank you!
Cool idea.
Thanks!
Thank you so much for this Tutorial. It's really specific for newbies front-end.
Thank you for your feedback!
Very cool and detailed.
The part on babel and webpack is a bit complex, maybe a tool like Vite, Snowpack or Esbuild could simplify thoses steps.