Shall we start with why and then move to how.
for most aspiring devs, webpack seems like this mysterious black box only assessable to the rock star coders among us. You may have seen the word is some sort of console logged javascript method with the name in all caps
if you make react apps (or any other JS libraries using webpack).
this is my attempt to demystify webpack and let the world know, with a little effort you can add webpack to your tool kit.
webpack is powerful because it breaks your code into what browsers can read from what browsers can't. A few examples are...
compiling SASS into CSS.
compiling React into ES5.
This is the topic I'm about to show you.
NOTE: if this is scaring you away I want you to know the syntactically webpack is just a javascript object.
PREREQUISITES:
unix command line experience and a unix operating system to follow along. I will be using a mac.
(if you don't have this you can figure out how to do this without it)
Understanding of the basics of React, javascript, javascript objects and JSON objects.
node needs to be installed. you can do so with homebrew (if you don't have homebrew follow the link and find the curl command on the page)
if you have brew installed you can run this command.
brew install node
CREDITS:
This is tutorial is largely inspired by Nate Jensens's article on the subject. however I'm trying to improve upon the delivery and accessibility for less experienced devs.
Let's start by opening our command line and making a new folder where we want our webpack project.
mkdir webpack-stuff && cd webpack-stuff
NOTE: the && symbol lets us use multiple commands one after the other.(but only if the previous command worked.)
if you are using vscode, you can press control and the ~ right below the escape button at the same time to open a command-line, so you can see the project files and the command-line at the same time.
Inside the directory run the following command to make a package JSON file.
npm init -y
Press ENTER and you should see a package.json.
install webpack by this into the command line.
npm i webpack webpack-cli --save-dev
Almost every command in this tutorial is going to be used with the - save-dev flag at the end.
your package.json should now have a devDependencies section.
If you made a typo or you don't have a devDependencies section.
you can run this command then copy and paste the command above.
npm uninstall webpack webpack-cli
you can run npm uninstall for any of the commands that we enter.
Next, make the webpack.config.js
touch webpack.config.js
This is where the heavy lifting of our application will be.
It helps me to think of a webpack.config.js file like a garden and our code is like the nutrients for the soil.
Inside the webpack file we want to tell webpack where to start(entry:) and where to place the complied-code(output:)
module.exports = {
entry: '',
output: {},
}
Since we have to give a file for an entry point lets go back to the command line and make the directory to start looking like it does in create-react-app with an scr/ folder and an index.js file.
mkdir src && touch src/index.js
now that we have the entry point lets add that to the webpack.config.js
module.exports = {
entry: 'src/index.js',
output: {},
}
Let's tell webpack where to save this code once it's compiled.
To save the code we need to use a default node method called path.
At the top of the file, lets add that.
const path = require('path')
Now inside of the curly brackets for the key: value, pair for output. Add the following code.
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
This block of code will make a new folder called dist and inside the folder a file called main.js with the compiled code.
NOTE: These are now webpack defaults but it's really important to understand that this is what is happening and it will prove foundational to helping you develop custom modifications later.
Now lets make a module section of our exports object underneath the }, of our output object.
module: {}
inside of a module: {… [rules go here] …} you can define an array of rules for webpack to follow.
The nutrients of the soil if you will.
Lets add that.
rules: []
The first one is a dependency called babel-loader. It will define how to interpret some elements of javascript
lets go to the command line and do that right now.
npm i babel-loader --save-dev
inside of our rules array we need to tell each dependency how to behave in babel-loader's case we want to.
- test: for a specific file type with a regular expression.
- exclude: doing this for node_modules folder.
- use: {loader: } in this case babel-loader
Inside of our rules array we want an object that looks like this.
{
test: /\.js$/,
exclude: /node_modules/,
use: { loader: 'babel-loader'}
},
in the test: section is a regrex to test for .js and the $ tells it to test for the ending of a file name.
our code is looking like this.
This is a lot of code and dependencies if you follow along this will work.
we are going to add the next few rules for .html .css and image files.
inside of the rules array go ahead and add this object
{
test: /\.html$/,
use: { loader: "html-loader"}
},
then run this in the command line.
npm i html-loader --save-dev
now for css
{
test: /\.css$/,
use: ['css-loader', 'style-loader']
},
the command to install
npm i css-loader style-loader --save-dev
to do this for images we need to add more testing because images come with more file endings then other files we want to test for, but we want them all to use the same loaders. we don't want to compile images, instead we want to save them as is.
{
test: /\.(jpg|jpeg|png|gif)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: './resources/images',
name: '[name].[ext]'
}
}
]
}
the | symbol indicates an or statement the options key gives us access to just save the images in the compiled folder 'dist' and declare a new path. The [name].[ext] lets us keep the file name and extension you may have seen similar syntax with form handling in JS.
lets install the file-loader
npm i file-loader --save-dev
our file thus far should look like this…
const path = require('path')
module.exports = {
entry: 'src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: { loader: 'babel-loader'}
},
{
test: /\.html$/,
use: { loader: "html-loader"}
},
{
test: /\.css$/,
use: ['css-loader', 'style-loader']
},
{
test: /\.(jpg|jpeg|png|gif)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: './resources/images',
name: '[name].[ext]'
}
}
]
}
]
}
}
now we get into babel and how it is used in this process.
lets make a .babelrc by running this command.
touch .babelrc
we are going to make a babel configuration to be best friends with webpack and with some syntactic changes.
All the rest of the npm i commands for babel will start with @
we set up our .babelrc file with a single json object.
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
lets run the npm installs for the packages that these represent.
npm i @babel/preset-env @babel/preset-react @babel/plugin-proposal-class-properties --save-dev
@babel/preset-env: helps manage javascript syntax transformations
@babel/preset-react: this is a bundle of other packages to compile react specific syntax into ES5 giving us the ability to use JSX and other react functionality.
@babel/plugin-proposal-class-properties: this gives us the ability to use arrow functions in classes with auto binding and ES6 syntax.
we need to install one more that called @babel/core. this is the landing platform for all babel configurations.
npm i @babel/core --save-dev
we have babel and webpack working together, so now let's give them something to work with.
to do this we need a couple more installs and a little more code in our webpack.config.js
add the html-webpack-plugin
npm install --save-dev html-webpack-plugin
then at the top of the webpack.config.js require the dependency.
const HtmlWebpackPlugin = require('html-webpack-plugin')
jump to the bottom of the file and above the last curly bracket write this code.
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: './index.html'
})
],
the plugins looks a little different from the rest because this is telling webpack where to display all of the compiled code. The rest of this file was saying where to start compiling, what to do when it found different kinds of files and where to keep the code it compiled.
you'll notice that the template and the file name need to be created and then we should make it look like a regular .html file.
mkdir public && touch public/index.html
if your text editor has emmet you can just write ! and press tab then #root tab inside the
to make the file but for the rest us you can copy this.<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>custom webpack</title>
</head>
<body>
<!-- dont forget the root id!!!!!!!! -->
<div id="root"></div>
</body>
</html>
now we have to create our index.js to append our whole application to the root id.
this is going to look almost exactly like an index.js file in create-react-app but without the fluff.
lets start by installing react and reactDOM.
npm i react react-dom
notice we didnt do the save dev. thats because these will work in production as well.
now lets import them in our index.js at the top of the file.
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
now we are ready to make an App.js to be the platform for the rest of our components.
we are going to use a reactDOM method called render which takes 2 arguments. 1.what we are rendering 2. where we are displaying it.
ReactDOM.render(<App />, document.getElementById('root'))
lets make an App.js Component.
touch src/App.js
fill it with the normal things we would for the start of a react app
import React from 'react';
const App = () => {
return (
<div>
my first webpack!!!!!!
</div>
);
};
export default App;
now lets go back to our package.json and add the start and build scripts
under the scripts section right under
"test" "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --open --mode development",
"build": "webpack --mode production"
one more install to get the script to work
npm i webpack-dev-server --save-dev
this allows you to actually run a server. When you run npm start you should see this in the browser.
It might not look like a lot now but you can make this application do anything a react app can do and more because you can add further configuration from here. and you can deploy this as a live website if you enter this into the command line.
npm run build
look at all the pretty colors!!
Conclusion:
webpack is a very powerful thing to have in your tool kit as a developer and now you have an idea of the basics. I encourage you to try and come up with your own order of install and code and practice a couple of times until you can do this from memory.
I think that the constant referencing of material in development will slow you down as a dev in the long run and so I'm going to tell you that I do get caught in the syntax and learn them but I also try and pick out the most relevant things to learn.
webpack is one of them.
please comment if you liked it.
Link to the github. https://github.com/TallanGroberg/webpack-for-react
Top comments (2)
Great article!
Made a complete list of workshops to learn the tricky parts of webpack
thanks Antoine!! I skimmed through the workshop and want to give it a closer look when I can.