- Install Webpack
npm install webpack --save
- Install babel-core, babel-loader, babel-preset-react, babel-preset-es2015
npm install babel-core babel-loader babel-preset-react babel-preset-es2015 --save
- Create a file named webpack.config.js in the root of the project. This is where webpack will look for the configuration
- In webpack.config.js add the following:
const path = require('path');
const appSrc = path.join(__dirname, 'src');
let config = {
entry: './src/index.js',
output: {
path: './build',
filename: 'js/bundle.js'
},
// How should webpack resolve paths in import statements
resolve: {
root: path.join(__dirname, 'node_modules'), // If not relative, fallback to node_modules
// If not relative and matches components, images, fonts use these paths
alias: {
components: path.join(appSrc, 'components'),
images: path.join(appSrc, 'assets', 'images'),
fonts: path.join(appSrc, 'assets', 'fonts')
}
},
module: {
loaders: [
// Transform javascript files using babel loader
{
test: /\.(js|jsx)$/,
include: appSrc,
loader: 'babel',
query: {
presets: ['es2015', 'react'] // babel understands es6 and JSX syntax
}
}
]
};
module.exports = config;
- Add a build script to package.json:
"scripts": {
"build": "webpack"
}
- Run the build script
npm run build
- Create an index.html file in the root of the project:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack Tutorial - Basic</title>
<script src="/js/bundle.js" charset="utf-8"></script>
</head>
<body>
<div id="root">
<!-- Yield to React -->
</div>
</body>
</html>
- Install webpack-dev-server
npm install webpack-dev-server --save
- Add a script to your package.json file
"scripts": {
"start": "webpack-dev-server --port=3000 --inline --hot"
}
- Run the script:
npm start
- Visit the site at http://localhost:3000/
- Install the style and css loaders
npm install style-loader css-loader
- Modify webpack.config.js to use the style and css loader
const path = require('path');
const appSrc = path.join(__dirname, 'src');
let config = {
entry: './src/index.js',
output: {
path: './build',
filename: 'js/bundle.js'
},
// How should webpack resolve paths in import statements
resolve: {
root: path.join(__dirname, 'node_modules'), // If not relative, fallback to node_modules
// If not relative and matches components, images, fonts use these paths
alias: {
components: path.join(appSrc, 'components'),
images: path.join(appSrc, 'assets', 'images'),
fonts: path.join(appSrc, 'assets', 'fonts')
}
},
module: {
loaders: [
// Transform javascript files using babel loader
{
test: /\.(js|jsx)$/,
include: appSrc,
loader: 'babel',
query: {
presets: ['es2015', 'react'] // babel understands es6 and JSX syntax
}
},
// Load css in javascript
{
test: /\.css$/,
include: appSrc,
loader: 'style!css' // or loaders: ['style', 'css']
}
]
};
module.exports = config;
- Uncomment the import statements for css files in src/components/App.js, src/components/TodoForm.js and src/components/TodoItem.js. Don't uncomment the lines importing images yet.
App.js:
import React, {Component} from 'react';
import TodoList from './TodoList';
import TodoForm from './TodoForm';
import 'components/App.css';
// import smiley from 'images/smiley.png';
TodoForm.js:
import React, {Component} from 'react';
import 'components/TodoForm.css';
TodoItem.js:
import React, {Component} from 'react';
import 'components/TodoForm.css';
- Assuming you are still running the dev server, you should be able to see the new styles at http://localhost:3000/
- Install file-loader
npm install file-loader
-
Modify webpack.config.js
const path = require('path'); const appSrc = path.join(__dirname, 'src'); let config = { entry: './src/index.js', output: { path: './build', filename: 'js/bundle.js' }, // How should webpack resolve paths in import statements resolve: { root: path.join(__dirname, 'node_modules'), // If not relative, fallback to node_modules // If not relative and matches components, images, fonts use these paths alias: { components: path.join(appSrc, 'components'), images: path.join(appSrc, 'assets', 'images'), fonts: path.join(appSrc, 'assets', 'fonts') } }, module: { loaders: [ // Transform javascript files using babel loader { test: /\.(js|jsx)$/, include: appSrc, loader: 'babel', query: { presets: ['es2015', 'react'] // babel understands es6 and JSX syntax } }, // Load css in javascript { test: /\.css$/, include: appSrc, loader: 'style!css' }, // Output font files to build folder { test: /\.(otf|ttf|woff|woff2|eot|svg)/, include: appSrc, loader: 'file', query: { name: 'fonts/[name].[hash:8].[ext]' } }, // Output images to the build folder { test: /\.(jpg|ico|png|gif)$/, include: appSrc, exclude: /favicon.ico$/, loader: 'file', query: { name: 'images/[name].[hash:8].[ext]' } }, // Output favicon to the root of the build folder { test: /favicon.ico$/, include: appSrc, loader: 'file', query: { name: 'favicon.ico' } } ] } }; module.exports = config;
-
Uncomment the import statements in src/index.js
import React from 'react';
import {render} from 'react-dom';
import App from 'components/App';
import './index.css';
import 'images/favicon.ico'
window.onload = function () {
let root = document.getElementById('root');
render(<App />, root);
};
- Uncomment the import statement and the image tag in src/components/App.js
import React, {Component} from 'react';
import TodoList from './TodoList';
import TodoForm from './TodoForm';
import 'components/App.css';
import smiley from 'images/smiley.png';
class App extends Component {
constructor (props) {
super(props);
this.state = {
todos: []
};
this._idSeq = 0;
// Bind Methods
this.addTodo = this.addTodo.bind(this);
this.toggleComplete = this.toggleComplete.bind(this);
this.removeTodo = this.removeTodo.bind(this);
}
addTodo (description) {
let id = ++this._idSeq;
this.setState({
todos: [...this.state.todos, {id: id, description: description, isComplete: false}]
});
}
toggleComplete (id) {
let newTodos = this.state.todos.map(todo => {
if (todo.id === id) {
return {
id: id,
description: todo.description,
isComplete: !todo.isComplete
};
} else {
return todo;
}
});
this.setState({todos: newTodos});
}
removeTodo (id) {
let newTodos = this.state.todos.filter(todo => todo.id !== id);
this.setState({todos: newTodos});
}
render () {
return (
<div className='app'>
<div className='page-header'>
<h1>
<img src={smiley} alt='smiley' width="32px" height="32px" />
Todo List Demo
</h1>
</div>
<TodoForm addTodo={this.addTodo} />
<TodoList
todos={this.state.todos}
toggleComplete={this.toggleComplete}
removeTodo={this.removeTodo}
/>
</div>
);
}
}
module.exports = App;
- Make sure the dev server is running and you should be able to see the images and fonts you added at http://localhost:3000/
- Modify webpack.config.js:
const webpack = require('webpack');
const path = require('path');
const appSrc = path.join(__dirname, 'src');
let config = {
entry: './src/index.js',
output: {
path: './build',
filename: 'js/bundle.js'
},
// How should webpack resolve paths in import statements
resolve: {
root: path.join(__dirname, 'node_modules'), // If not relative, fallback to node_modules
// If not relative and matches components, images, fonts use these paths
alias: {
components: path.join(appSrc, 'components'),
images: path.join(appSrc, 'assets', 'images'),
fonts: path.join(appSrc, 'assets', 'fonts')
}
},
module: {
loaders: [
// Transform javascript files using babel loader
{
test: /\.(js|jsx)$/,
include: appSrc,
loader: 'babel',
query: {
presets: ['es2015', 'react'] // babel understands es6 and JSX syntax
}
},
// Load css in javascript
{
test: /\.css$/,
include: appSrc,
loader: 'style!css'
},
// Output font files to build folder
{
test: /\.(otf|ttf|woff|woff2|eot|svg)/,
include: appSrc,
loader: 'file',
query: {
name: 'fonts/[name].[hash:8].[ext]'
}
},
// Output images to the build folder
{
test: /\.(jpg|ico|png|gif)$/,
include: appSrc,
exclude: /favicon.ico$/,
loader: 'file',
query: {
name: 'images/[name].[hash:8].[ext]'
}
},
// Output favicon to the root of the build folder
{
test: /favicon.ico$/,
include: appSrc,
loader: 'file',
query: {
name: 'favicon.ico'
}
}
]
},
plugins: [
new webpack.SourceMapDevToolPlugin()
]
};
module.exports = config;
- When you restart the dev server you will have a source map
- Install extract-text-webpack-plugin
npm install extract-text-webpack-plugin
- Modify your webpack.config.js:
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const appSrc = path.join(__dirname, 'src');
let config = {
entry: './src/index.js',
output: {
path: './build',
filename: 'js/bundle.js'
},
// How should webpack resolve paths in import statements
resolve: {
root: path.join(__dirname, 'node_modules'), // If not relative, fallback to node_modules
// If not relative and matches components, images, fonts use these paths
alias: {
components: path.join(appSrc, 'components'),
images: path.join(appSrc, 'assets', 'images'),
fonts: path.join(appSrc, 'assets', 'fonts')
}
},
module: {
loaders: [
// Transform javascript files using babel loader
{
test: /\.(js|jsx)$/,
include: appSrc,
loader: 'babel',
query: {
presets: ['es2015', 'react'] // babel understands es6 and JSX syntax
}
},
// Load css in external stylesheet
// Need publicPath to resolve relative paths of urls in css
{
test: /\.css$/,
include: appSrc,
loader: ExtractTextPlugin.extract('style-loader','css-loader', {publicPath: '/'})
},
// Output font files to build folder
{
test: /\.(otf|ttf|woff|woff2|eot|svg)/,
include: appSrc,
loader: 'file',
query: {
name: 'fonts/[name].[hash:8].[ext]'
}
},
// Output images to the build folder
{
test: /\.(jpg|ico|png|gif)$/,
include: appSrc,
exclude: /favicon.ico$/,
loader: 'file',
query: {
name: 'images/[name].[hash:8].[ext]'
}
},
// Output favicon to the root of the build folder
{
test: /favicon.ico$/,
include: appSrc,
loader: 'file',
query: {
name: 'favicon.ico'
}
}
]
},
plugins: [
new webpack.SourceMapDevToolPlugin(),
new ExtractTextPlugin('css/styles.css')
]
};
module.exports = config;
- Modify your index.html to load the stylesheet
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack Tutorial - Basic</title>
<script src="/js/bundle.js" charset="utf-8"></script>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="root">
<!-- Yield to React -->
</div>
</body>
</html>
- Restart the dev server and now your stylesheet can load before the javascript bundle