DEV Community

Cover image for Learn Webpack by dumping create-react-app for once.
Tushar Kashyap
Tushar Kashyap

Posted on • Edited on

Learn Webpack by dumping create-react-app for once.

So I have been reading Learning React by Alex Banks and Eve Porcello lately and in a chapter they went through the whole process of setting up a React project without using create-react-app, and I felt it is a great thing to know about how stuff works under the hood.

For everyone who doesn't have access to the book I thought it will be a good idea to write on the topic as articles are highly accessible.

P.S. The book is worth the money nonetheless.

Also, I am assuming that you all must be knowing how to use the node package manager (npm) that comes with nodejs.

Now let's see how can we create our own React application

1. Project Setup

Start with initializing the package.json file with npm init so we can install modules. We will install the initial dependencies we need to get started.

npm init -y
npm install --save react react-dom serve
Enter fullscreen mode Exit fullscreen mode

Now let's start working on setting up a skeleton (more like a folder structure). I recommend following along if you are a beginner or if you have never used webpack before.

> node_modules
> package.json
> package-lock.json
> /src (folder)
    > index.js
    > /components (folder)
      > App.js
      > Banner.js
      > Header.js
Enter fullscreen mode Exit fullscreen mode

This is our project folder, it has a very basic setup consisting of a src folder which has the index.js file (this will be our entry point) and a components folder containing App, Banner and Header components.

There are many ways you can structure your app, this is just a very basic, easy to understand structure.

2. Making the Project

Our project will have a header and banner. So let's start by making the Header component.

// ./src/components/Header.js
import React from 'react';

export default function Header() {
  return (
    <header>
      <h2>Checkout these lovely Unicorns</h2>
    </header>
  );
}
Enter fullscreen mode Exit fullscreen mode

and now the Banner component

// ./src/components/Banner.js
import React from 'react';

export default function Banner() {
  return (
    <section>
      πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„Unicorns For SALEπŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

Add these components to App.js and then render App component through index.js using ReactDOM.render().

// ./src/components/App.js
import React from 'react';
import Header from './Header';
import Banner from './Banner';

export default function App() {
  return (
    <>
      <Header />
      <Banner />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode
// ./src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';

ReactDOM.render(<App />, document.getElementById('root'));
Enter fullscreen mode Exit fullscreen mode

So now we have completed writing our React components, let's move on to the next step.

3. Setting up Webpack

As our components are split into different modules we will be using webpack which is a module bundler. We cannot write everything directly into one file as that makes development messy so we use a module bundler that bundles everything into one big file that we can use for production.

Let's start by installing webpack and webpack-cli as dev dependencies.

npm install webpack webpack-cli --save-dev
Enter fullscreen mode Exit fullscreen mode

So understanding webpack is a topic of it's own but I'll try to give a high level overview of it. You can see we have import statements all over the place, so webpack will start looking from the entry point we will define in the webpack.cofig file (index.js) and start building a dependency graph as soon it hits any import statements. In a casual language webpack is saying like "Oh, this module needs that module and hey that module needs another module". So it'll bundle everything into one big file.

Now let's build our very basic webpack.config.js file

// ./webpack.config.js
var path = require("path");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js"
  }
};
Enter fullscreen mode Exit fullscreen mode

This config file has an entry point and an output path, we are telling webpack to start looking from index.js and bundle everything (on the basis of dependency graph) and put the output in a folder named dist in a file named bundle.js, we have also set the mode to development so it will not do any minification and production stuff as of now.

So now webpack is ready to bundle our files but as we are writing JSX and using modern JavaScript we would want a loader which will covert these things into a syntax that every browser (new or old) can understand. We will be using the babel loader here.

npm install babel-loader @babel/core --save-dev
Enter fullscreen mode Exit fullscreen mode

and now we will update the webpack config file to use this loader.

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js"
  },
  module: {
    rules: [{ test: /\.js$/, exclude: /node_modules/, use: "babel-loader" }]
  }
};
Enter fullscreen mode Exit fullscreen mode

The rules property is the place where you will be adding all the loaders (which are JavaScript objects) for various purposes (we will also add sass loader in the end).
Here we are only adding the babel loader which has a test property telling if you see any .js file use the babel loader on it except if the file is a part of the node_modules folder.

Webpack is all set now. The last thing will be to make a .babelrc file specifying the presets telling Babel which transforms to perform. As here we need to transform ESNext (modern JS) syntax to a syntax understandable by all browsers and also transform JSX too. We can do that by the following.

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

4. Running webpack

As we installed the webpack-cli you can run webpack from the terminal like this

npx webpack
Enter fullscreen mode Exit fullscreen mode

This will make a dist directory and create the bundle.js in development mode as we stated in the config file. Change the mode to production when you are ready to ship or a common practice is to make scripts in package.json.

// ./package.json
...
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --mode production"
 },
...
Enter fullscreen mode Exit fullscreen mode

Do npm run build in this case.

5. Making the HTML file

Here we will be making the index.html which will link to the bundle.js file. I'm making it in the dist folder.

// ./dist/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Unicorn Stable</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="./bundle.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

The final folder structure would look like this

> node_modules
> package.json
> package-lock.json
> /dist (folder)
    > bundle.js
    > index.html
> /src (folder)
    > index.js
    > /components (folder)
      > App.js
      > Banner.js
      > Header.js
Enter fullscreen mode Exit fullscreen mode

Now, open the HTML file in the browser and you will see that if everything went well we have the Unicorn sale ongoing πŸ¦„.

6. Adding sass

Let's add some styles to our components. We will be using a single sass file but you are free to use separate sass files for each component as we just have to import it and webpack will bundle it for us using appropriate loaders.

/* ./src/styles.scss */
h2 {
  background-color: #a0c3f0;
  font-size: 200%;
  text-align: center;
}

section {
  border: 2px dotted #ac307c;
  font-size: 150%;
  text-align: center;
  padding: 1em;
}
Enter fullscreen mode Exit fullscreen mode

and then we import them into our App component.

import React from 'react';
import Header from './Header';
import Banner from './Banner';
import '../styles.scss';
....
Enter fullscreen mode Exit fullscreen mode

Let's now install the necessary loaders, we will need style-loader, css-loader, sass-loader (which depends on node-sass).

npm install --save-dev style-loader css-loader sass-loader node-sass
Enter fullscreen mode Exit fullscreen mode

and then we update the webpack config file to check for .scss files and apply the loaders in a set order.

var path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      { test: /\.js$/, exclude: /node_modules/, use: 'babel-loader' },
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
      },
    ],
  },
};
Enter fullscreen mode Exit fullscreen mode

The order is to be kept the same as sass-loader needs to be applied first then css-loader and style-loader at the end (the last one in applied first).

Let's bundle our code one last time with npx webpack and check the browser. All the styles would have been applied :)

Top comments (2)

Collapse
 
dakshguru_ profile image
Daksh Guru

Very well written, will try it out 😊

Collapse
 
tusharkashyap63 profile image
Tushar Kashyap

Thank you Daksh ☺️