In this final post of our series on CSS Modules, I’ll be taking a look at how to make a static React site with the thanks of Webpack. This static site will have two templates: a homepage and an about page with a couple of React components to explain how it works in practice.
In the previous post we set up a quick project with Webpack that showed how dependencies can be imported into a file and how a build process can be used to make a unique class name that is generated in both CSS and HTML. The following example relies heavily on that tutorial so it’s definitely worth working through those previous examples first. Also this post assumes that you’re familiar with the basics of React.
In the previous demo, there were problems with the codebase when we concluded. We depended on JavaScript to render our markup and it wasn’t entirely clear how we should structure a project. In this post we’ll be looking at a more realistic example whereby we try to make a few components with our new Webpack knowledge.
To catch up, you can check out the css-modules-react repo I’ve made which is just a demo project that gets us up to where the last demo left off. From there you can continue with the tutorial below.
Webpack’s Static Site Generator
To generate static markup we’ll need to install a plugin for Webpack that helps us generate static markup:
npm i -D static-site-generator-webpack-plugin
Now we need to add our plugin into `webpack.config.js` and add our routes. Routes would be like / for the homepage or /about for the about page. Routes tell the plugin which static files to create.
var StaticSiteGeneratorPlugin = require('static-site-generator-webpack-plugin');
var locals = {
routes: [
'/',
]
};
Since we want to deliver static markup, and we’d prefer to avoid server side code at this point, we can use our StaticSiteGeneratorPlugin. As the docs for this plugin mentions, it provides:
a series of paths to be rendered, and a matching set of index.html files will be rendered in your output directory by executing your own custom, webpack-compiled render function.
If that sounds spooky hard, not to worry! Still in our `webpack.config.js`, we can now update our module.exports object:
module.exports = {
entry: {
'main': './src/',
},
output: {
path: 'build',
filename: 'bundle.js',
libraryTarget: 'umd' // this is super important
},
...
}
We set the libraryTarget because that’s a requirement for nodejs and the static site plugin to work properly. We also add a path so that everything will be generated into our `/build` directory.
Still inside our `webpack.config.js` file we need to add the StaticSiteGeneratorPlugin at the bottom, like so, passing in the routes we want to generate:
plugins: [
new ExtractTextPlugin('styles.css'),
new StaticSiteGeneratorPlugin('main', locals.routes),
]
Our complete `webpack.config.js` should now look like this:
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var StaticSiteGeneratorPlugin = require('static-site-generator-webpack-plugin')
var locals = {
routes: [
'/',
]
}
module.exports = {
entry: './src',
output: {
path: 'build',
filename: 'bundle.js',
libraryTarget: 'umd' // this is super important
},
module: {
loaders: [
{
test: /.js$/,
loader: 'babel',
include: __dirname + '/src',
},
{
test: /.css$/,
loader: ExtractTextPlugin.extract('css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'),
include: __dirname + '/src'
}
],
},
plugins: [
new StaticSiteGeneratorPlugin('main', locals.routes),
new ExtractTextPlugin("styles.css"),
]
};
In our empty `src/index.js` file we can add the following:
// Exported static site renderer:
module.exports = function render(locals, callback) {
callback(null, '<html>Hello!</html>');
};
For now we just want to print Hello! onto the homepage of our site. Eventually we’ll grow that up into a more realistic site.
In our `package.json`, which we discussed in the previous tutorial, we already have the basic command, webpack, which we can run with:
npm start
And if we check out our build directory then we should find an index.html file with our content. Sweet! We can confirm that the Static Site plugin is working. Now to test that this all works we can head back into our webpack.config.js and update our routes:
var locals = {
routes: [
'/',
'/about'
]
};
By rerunning our npm start command, we’ve made a new file: `build/about/index.html`. However, this will have “Hello!” just like `build/index.html` because we’re sending the same content to both files. To fix that we’ll need to use a router, but first we’ll need to get React set up.
Before we do that we should move our routes into a separate file just to keep things nice and tidy. So in `./data.js` we can write:
module.exports = {
routes: [
'/',
'/about'
]
}
Then we’ll require that data in `webpack.config.js` and remove our locals variable:
var data = require('./data.js');
Further down that file we’ll update our StaticSiteGeneratorPlugin:
plugins: [
new ExtractTextPlugin('styles.css'),
new StaticSiteGeneratorPlugin('main', data.routes, data),
]
Installing React
We want to make lots of little bundles of HTML and CSS that we can then bundle into a template (like an About or Homepage). This can be done with react, and react-dom, which we’ll need to install:
npm i -D react react-dom babel-preset-react
Then we’ll need to update our `.babelrc` file:
{
"presets": ["es2016", "react"]
}
Now in a new folder, `/src/templates`, we’ll need to make a `Main.js` file. This will be where all our markup resides and it’ll be where all the shared assets for our templates will live (like everything in the and our site’s