[UPDATE] There is now an easier way to do this. Check out this forum post for more information.

Due to its popularity in the React community, Webpack is all the rage. It gives you lightning fast rebuilds, hot module replacement, and performance boosts from modularity and multiple entry points. Up until recently, getting it to work with Meteor was somewhere between a hack and generally a bad idea.

But a hero named Benoit Tremblay made a package that allows Webpack to seamlessly integrate with Meteor. Benoit, if you’re reading this, know that your work is appreciated!

There are a couple downsides to using Webpack, but they are small. For one, there is more boilerplate code than the average Meteor developer is used to. The only other downside is that it takes a while to get the hang of how Webpack takes advantage of modules.

You can use one of the kickstart projects and start from there, but I’d like to start from an even more basic example to understand Webpack from the ground up. This is how you use Webpack to create a counter app like you’d get from the command meteor create myapp. The repo of the final app can be found here. For more on code splitting, check out this article.

A most basic counter app. The image above is using Meteor, Webpack, and React.

Step 1

Create a new app. I’ll call mine webpack:

$ meteor create webpack

Then add webpack:webpack and remove ecmascript, because Webpack will handle this for us.

$ meteor add webpack:webpack

$ meteor remove ecmascript

Step 2

Now we need to set up the file structure. We need a client and a server folder.

$ mkdir client server

Then we need to create a few files. We need a webpack.packages.json file to keep track of all our NPM dependencies, and we need a few webpack.conf.js files to configure Webpack. We will also need files, which we will call entry.jsx.

$ touch webpack.packages.json client/webpack.conf.js server/webpack.conf.js client/entry.jsx server/entry.js

And while we’re at it, delete webpack.js and webpack.css from the root, and replace everything in webpack.html with the following:

<head>

<title>webpack</title>

</head> <body>

<div id="app"></div>

</body>

Step 3

Now we need to set up some of our configuration files. First, since this is a minimalist example, we’re just going to add Babel and React as dependencies in the webpack.packages.json file.

With the most recent version, there may be some additional configuration necessary. See the comment by Ben Strahan if the configuration below does not work for you.

{

"react": "^0.14.2",

"react-dom": "^0.14.2",



"babel-loader": "^5.3.2"

}

You’ll notice that React is split into react and react-dom. This is a change that took place in the 0.14.0 update, which split the global into many smaller pieces.

Step 4

Configure Webpack with the webpack.conf.js files. It’s worth noting that these are JavaScript files, so you can run actual code in them if necessary. In both our /client and /server directories, add the following code:

module.exports = {

entry: './entry',

resolve: {

extensions: ['', '.js', '.jsx', '.json']

},

module: {

loaders: [

{ test: /\.jsx?$/,

loader: 'babel',

exclude: /node_modules/ }

]

}

};

In the code above, the entry is “the entry point for the bundle”. I’ll explain what that means below.

Allowing for multiple entry points is one of the things that makes Webpack so performant. For example, if you had two fairly distinct parts of your app—let’s say, a chatroom and a settings page—you shouldn’t need to load both pieces as soon as you open the app. Entry points allow you to load only the pieces that are needed, when they are needed, giving a nice performance boost.

The next thing to look at is resolve.extensions, which is actually quite simple. This bit of code tells Webpack which files to look for based on their extensions, which has the added benefit of making it unnecessary to add the extension in the string we pass to entry.

The last thing is the array of module.loaders, which are another powerful tool in Webpack. In this case, we are using a babel-loader, which finds all .jsx files and transforms them into JavaScript. Loaders can be used to compile Sass, Coffeescript, and perform more complex transformations, such as converting images to inline if they are below a certain size.

Step 5

At this point, you should be able to run Meteor without any problems.

Now within entry.jsx in our client, we need to build our React component. First, at the top of the file, we need to declare our dependencies.

import ReactDOM from 'react-dom';

import React from 'react';

Then we need to create the Counter component that we intend to render. We start by setting the initial state of counter to zero and creating a function that adds one to the state. We then attach that increment function to the button and show the state of the counter in the same place we would in the default Meteor app.

let Counter = React.createClass({

getInitialState() {

return {

counter: 0

}

},

addToCounter() {

this.setState(function(previousState) {

return { counter: previousState.counter + 1 };

})

},

render() {

return (

<div>

<h1>Welcome to Meteor!</h1>

<button

onClick={this.addToCounter}

>Click Me</button>

<p>You've pressed the button {this.state.counter} times.</p>

</div>

)

}

})

And the last thing we need to do is render the component to the DOM. Below the component we just created, add the following:

Meteor.startup(function() {

ReactDOM.render(

<Counter />,

document.getElementById('app')

)

})

And now you have yourself a counter app using Meteor and Webpack! From here, you should be able to piece together what is happening in the kickstart projects, which are more complex.

Conclusion

Webpack gives you blazingly fast rebuilds, easier integration with NPM, and code modularity. I expect I will continue to use it in future projects.

I also wonder if there is any value in integrating Webpack into Meteor in a more formal way. This package does good job of integrating the two technologies and it does add a level of complexity that is likely unnecessary for basic Meteor apps. I’m curious to hear your thoughts on the subject!