DEV Community

loading...
Cover image for Creating your React project from scratch without create-react-app: The Complete Guide.

Creating your React project from scratch without create-react-app: The Complete Guide.

_CODE
WebDev from zero to hero 🖥 🖤 Instagram + Twitter: @underscorecode
・9 min read

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
Enter fullscreen mode Exit fullscreen mode

Once created, we're going to navigate into it.

cd my-react-project
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
node -v
Enter fullscreen mode Exit fullscreen mode

Once we have npm and Node.js ready to use on our computer, we're going to run the following command:

npm init
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then, just add the following code snippet:

.babelrc

{
  "presets": [
    "@babel/preset-react"
  ]
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
npm install --save-dev nodemon
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
    }),
  ]
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
      }
   });
});
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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"));
Enter fullscreen mode Exit fullscreen mode

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",
    }
  ...
}
Enter fullscreen mode Exit fullscreen mode

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"
  ...
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

And then, just run it.

npm run dev
Enter fullscreen mode Exit fullscreen mode

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.

Discussion (24)

Collapse
hasnaindev profile image
Muhammad Hasnain • Edited

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.

Collapse
nikhilmwarrier profile image
Nikhil M Warrier

I was about to mention just this
Great article though @underscorecode

Collapse
icecoffee profile image
atulit023 • Edited

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.

Collapse
underscorecode profile image
_CODE Author

I'm glad you found it useful.

Collapse
khorne07 profile image
Khorne07

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.

Collapse
underscorecode profile image
_CODE Author

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:


{
        test: /\.css$/i,
        use: [
          "style-loader",
          "css-loader",
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  [
                    "postcss-preset-env",
                    {
                      // Options
                    },
                  ],
                ],
              },
            },
          },
        ],
      },

Enter fullscreen mode Exit fullscreen mode

Note that you’ll also need to install postcss-preset-env.

Happy coding!

Collapse
khorne07 profile image
Khorne07

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

Collapse
theantipioneer profile image
Atang

I got this error after following everything:
PS C:\Users\USER\projects\reactProj\react-manual> npm run build

react-manual@1.0.0 build
rm -rf dist && webpack --mode development

'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

react-manual@1.0.0 build
rm -rf dist && webpack --mode development

'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?

Collapse
vishal2369 profile image
Vishal2369

Nice and informative

Collapse
underscorecode profile image
_CODE Author

Thank you!

Collapse
andrewbaisden profile image
Andrew Baisden

Cool idea.

Collapse
underscorecode profile image
_CODE Author

Thanks!

Collapse
kiditran profile image
Kidi Tran

Thank you so much for this Tutorial. It's really specific for newbies front-end.

Collapse
underscorecode profile image
_CODE Author

Thank you for your feedback!

Collapse
slashgear_ profile image
Antoine Caron

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.

Collapse
drsimplegraffiti profile image
Abayomi Ogunnusi

This is very clear and to the point ....a pdf download will be appreciated

Collapse
underscorecode profile image
_CODE Author

Thanks for your feedback!

Collapse
enriquesource profile image
EnriqueSource

And I really liked the inclusion of express.js.

Collapse
enriquesource profile image
EnriqueSource

Thank you very much!. A really interesting and informative post. I would like to read more posts like this one. Thanks!

Collapse
underscorecode profile image
_CODE Author

Happy to hear you found it interesting. Thanks for your feedback!

Collapse
naolchala profile image
naolchala

or you could just use parcel instead of this all configuration

Collapse
willaiem profile image
Damian Żygadło

or vite

Collapse
karx1 profile image
Yash Karandikar

You don't need express for this though, you can just open the HTML file in a browser.

Collapse
theantipioneer profile image
Atang

Wht did we have to bring in webpack in the config if we not gona use it ?