src/index.js
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<div>I am a React App</div>
);
You can start a dev server for development by running npx webpack serve --mode development
in the project folder. You can create a production build for deployment by running npx webpack --mode production
. The production build will be placed in a folder named build
. To easily run these commands, you can save them as npm scripts as shown in this post.
You can create React components and use them within the index.js
file by importing them. These components can import other React components and CSS files.
src/Title/Title.jsx
import './Title.css'
const Title = () => (
<h1>I am a title</h1>
)
src/Title/Title.css
h1 {
color: red;
}
src/index.js
import ReactDOM from 'react-dom/client';
import Title from './Title/Title.jsx';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Title />
);
Similarly, you can import image files into the React component. Webpack will copy these files to the production build and provide a relative URL to the component that imported the image.
import reactIcon from './react.svg';
const Icon = () => (
<img src={reactIcon} alt="React Logo" width={64} height={64} />
);
export default Icon;
Let's walk through the config file and see what each option does.
return {
entry: './src/index.js',
...
}
This tells Webpack the file it should start processing from. It will read this file and process each file that is imported by it. It will then repeat this process for each of the imported files until all the imports are resolved.
return {
...
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'build'),
},
...
}
This tells Webpack where to output the generated assets. In this case, it is set up to output the Javascript to a file called main.js
and to output all these assets to a folder called build
.
new HtmlWebpackPlugin({
template: path.join(__dirname, 'public', 'index.html'),
})
This plugin outputs a HTML file with the generated Javascript and CSS files referenced through script
and link
elements. Since React requires a root element to render into, we provide a template HTML file with a root element specified to the plugin (index.html
) which it extends with those elements.
new MiniCssExtractPlugin()
This plugin outputs all the imported CSS files into a single CSS file.
This section of the config tells Webpack what to do with each of the imported files. We do this by specifying rules that mention which loaders need to be used on each type of file.
[
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { targets: 'defaults' }],
['@babel/preset-react', { runtime: 'automatic' }]
]
}
}
},
...
]
This rules matches imported files with a .js
or .jsx
extension. It then asks Webpack to load these files using babel-loader
which then passes each file through Babel. We pass custom options to babel-loader
to pass to Babel and Babel will use these options while processing each file.
Babel presets are plugins that customize Babel's behavior. @babel/preset-env
allows Babel to output JS code that can run on a given set of browsers. This allows us to use new Javascript language features in the codebase before they are supported by browsers. In this case, we set @babel/preset-env
to transform the JS code into code that can run on the defaults
set of browsers (i.e. browsers with more than 0.5% of market share and the last two versions of actively developed browsers)
@babel/preset-react
is combination of plugins for transforming React component code into JS. Notably, it consists of @babel/plugin-transform-react-jsx
which allows Babel to convert JSX code into JS. By setting @babel/preset-react
to use the automatic
runtime, we ask @babel/plugin-transform-react-jsx
to use the new JSX transform where React does not have to be imported into each component file.
[
...,
{
test: /\.css$/i,
use: [
devMode ? "style-loader" : MiniCssExtractPlugin.loader,
"css-loader"
],
}
]
This rule matches imported files with a .css
extension. It then asks Webpack to first load these files using css-loader
which resolves the @import
and url()
statements in the CSS files. (Webpack applies loaders from the end to the start)
Next it asks Webpack to use style-loader
or MiniCssExtractPlugin's loader depending on the mode
parameter that is passed into the webpack command. We use style-loader
during development because it is faster than the MiniCssExtractPlugin.
[
...,
{
test: /\.(png|jpe?g|svg)$/i,
type: 'asset/resource'
}
]
This rule matches imported image files with a .png
, .jpeg
, .jpg
or .svg
extension. Webpack 5 has built in support for handling assets files that it should copy to the output folder. So we invoke this feature by telling Webpack to treat these files as asset/resource
. (In previous versions of Webpack, a separate loader has to be used e.g. file-loader
)
A Webpack configuration file can seem scary at first. So I hope that this post helped you be more confident in managing it. You can now extend this basic configuration in many ways such as adding support for SASS or adding support for environment variables.