DEV Community

Cover image for Create React project without CRA(or any build tool)
Vishal Sharma
Vishal Sharma

Posted on • Originally published at codeentity.tech on

Create React project without CRA(or any build tool)

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:

  1. Create a new directory for your project and navigate to it.

  2. Initialize a new npm package by running npm init and following the prompts. This will create a package.json file in your project directory.

  3. 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.

  4. 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>
Enter fullscreen mode Exit fullscreen mode
  1. 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"))
Enter fullscreen mode Exit fullscreen mode
  1. Run a local development server by installing a tool such as http-server and running http-server in your project directory.

  2. 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:

  1. 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.

  2. Install the Babel packages by runningnpm 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.

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

This configuration tells webpack to use the babel-loader to transpile your JavaScript code using the Babel presets for environment and React.

  1. 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>
Enter fullscreen mode Exit fullscreen mode
  1. Modify the package.json file to include the following scripts:
"scripts": {
    "build": "webpack --mode production",
  }
Enter fullscreen mode Exit fullscreen mode
  1. 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
Enter fullscreen mode Exit fullscreen mode

This script allow you to build your project for production using npm run build.

  1. Run npm run build to build your project. This will generate dist folder with bundle.js file.
  2. Now run index.html using http-server module or you can use live server extension if you are using vscode.
  3. 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",
    }),
  ],
}
Enter fullscreen mode Exit fullscreen mode

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.

  1. Run npm run build to build your project. This will generate dist folder with bundle.js and index.html file.
  2. You can run this index.html similarly using http-server module or you can use live server extension.
  3. There’s something still pending in this config which is we can not import css files to project.
  4. To setup this install style loader and css loader by running npm install --save-dev style-loader css-loader.
  5. 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"],
}
Enter fullscreen mode Exit fullscreen mode
  1. Create a style.css file and add some styles in it.
  2. Import this file in index.js
  3. 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.
  4. 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

  1. Install Webpack Dev Server using npm install --save-dev webpack-dev-server.
  2. 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",
}
Enter fullscreen mode Exit fullscreen mode
  1. We have some extra properties in this config.

  2. This configuration file can be used with the webpack-dev-server to start a development server for project. To use this add following script to package.json."start": "webpack-dev-server --mode development --config webpack.config.dev.js"

  3. Run npm run start or npm 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.

  1. Create a folder with name public at root and move index.html to this folder.

  2. Create a folder with name src at root and move index.js and styles.css to this folder.

  3. Also we need to change these paths webpack and webpack dev config.

After this activity your folder structure should look like: Folder Structure

Now we need to have a way to copy static assets like fonts and images to the bundle folder
  1. To copy static assets such as images, fonts, or other media files using webpack, you can use the copy-webpack-plugin and the file-loader.

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

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

  1. Copy an image.png file to public folder
  2. 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" />
  </>
)
Enter fullscreen mode Exit fullscreen mode
  1. 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/",
    },
  },
}

Enter fullscreen mode Exit fullscreen mode

Now you can import image file to js file using import syntax. Let’s try it out

  1. Copy a image file image2.png to src folder.
  2. Import file in index.js using following syntax.
import image from "./image.png"
Enter fullscreen mode Exit fullscreen mode
  1. Modify img tag to use the imported file
const element = (
  <>
    <h1>Hello World</h1>
    <img src={image2} />
  </>
)
Enter fullscreen mode Exit fullscreen mode

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

This will ignore the node_modules and dist folder as we don’t need to push these.

What Next

  • We can add sass or postcss support using loaders.
  • Extract out css to separate bundles and hash css bundles also.
  • Add svgr to import svg as react components.

Top comments (0)