An opinionated boilerplate template for creating a statically-generated site, using content managed in Contentful, with a build system backed by Metalsmith.
The build system builds a static site to the /build
folder, which can be deployed on your static host of choice. It's geared towards use on Netlify, but you can serve the files from any host that serves static content, including GitHub Pages.
This is still pretty new, so we're still working out some kinks, however we're using it on a number of production sites and it's been very robust.
Node.js 4+ (developed against v6.2 so YMMV). You're using nvm
right? Great — the repo is initialized with an .nvmrc
file so run nvm use
and you should be golden
# clone the repo
git clone https://github.com/centre-for-effective-altruism/contentful-metalsmith-boilerplate your_project_name
cd your_project_name
# rename default origin to upstream to pull in updates in future
git remote rename origin upstream
# create a new repo on github, and add it as your 'origin' remote
git remote add origin https://github.com/yourhandle/your_project_name
git push -u origin master
# install packages
npm install
Link the build system to Contentful by adding API keys to a .env
file. Run the following and create API keys (instructions below) as necessary.
npm run tools
# ? What do you want to do? (Use arrow keys)
# ...
# ❯ Create an Environment Variables file (.env)
- Create a new, empty Contentful space
- Go to the APIs tab and generate a new set of keys (or just use the default ones)
- You'll also need a content management token, which is under a tab on the APIs page. Click generate personal token. You could also create a new App and generate an OAuth Token and use that instead...
(Sensitive data like API keys should never be checked into source control. The .env
file is excluded by .gitignore
, so you should be fine, but you should be aware that that's where this data is being stored so you don't inadvertently check it in...)
To get started, it's a good idea to populate your Contentful Space with default Content Types and some Entries
Add default Content Types:
npm run tools
# ? What do you want to do? (Use arrow keys)
# ...
# ❯ Create default Content Types
Add default content:
npm run tools
# ? What do you want to do? (Use arrow keys)
# ...
# ❯ Add default content to space
Add a new Content Type:
npm run tools
# ? What do you want to do? (Use arrow keys)
# ...
# ❯ Create new Content Type
Then go to Contentful and add a few entries.
In config/site.json
, there will be a bunch of metadata set to a default that you will want to change.
Once you've got the site installed, you just have to build it!
npm run build
# builds static site to /build
(This command bundles scripts with Browserify, compiles SCSS partials into a single CSS file, then runs the Metalsmith build (see Build commands below for info on running these commands individually)
The static site is just a collection of HTML files, so any simple web server will do. We just use http-server
:
npm install -g http-server
- Open a new Terminal tab
- Run
cd build
(i.e. the build directory) - Run
http-server
(serves files at http://localhost:8080 by default)
Open up a browser and look at what you've done!
npm run build
— build everything (by default indevelopment
mode)npm run scripts
— just rebuild stylesnpm run styles
— just rebuild scriptsnpm run metalsmith
— rebuild static site using Metalsmith, skip rebuilding styles/scriptsnpm run staging
- run the build instaging
mode (essentially the same asproduction
but with more debugging). Alias forNODE_ENV=staging npm run build
npm run production
- run the build inproduction
mode (enables minification, file concatenation, CSS purification). Alias forNODE_ENV=production npm run build
To access the tools, run:
npm run tools
This will give you several options:
? What do you want to do? (Use arrow keys)
❯ Create new Metalsmith plugin
Create new Content Type
Delete Content Types
──────────────
Create default Content Types
Add default content to space
──────────────
Create an Environment Variables file (.env)
Creates a file with Metalsmith plugin boilerplate under lib/metalsmith/plugins
, and will helpfully print a require
call to the REPL that you can add to the main metalsmith.js
task file (located in tasks/metalsmith
).
Remember to also add the .use()
call to the Metalsmith build where you want your plugin to appear:
const insertEmojiRandomly = require(paths.lib('metalsmith/plugins/insert-emoji-randomly'))
...
metalsmith
.source('../src/metalsmith')
.destination('../build')
...
.use(insertEmojiRandomly())
(remember to actually call the plugin, and not just pass in the variable — the plugin boilerplate returns a function, so use(myPlugin)
won't work, you'll need .use(myPlugin())
)
The plugin boilerplate adds an options argument, which you can use to easily filter the type of files that will be passed to the plugin (using minimatch glob patterns)
const defaults = {
// set some default options here
filter: '**/*.html'
}
Uses the Contentful Content Management API to create a new Content Type in your Contentful Space, and adds the necessary local data to the build system to make sure that Entries will be included in the build. (If you want to hack around with these files, look in tasks/metalsmith/content-types
)
Setting the apperance of Contentful field types (like setting a Short Text field to Slug
or a List field to Radio
) is not possible over the API, so you'll need to do it manually throught the Contentful web interface.
If you've got a Content Type on Contentful you don't need any more, remove it using this command (which also removes local build files). This is a non-undoable, destructive action, so use this carefully!
Most sites use the same few basic Content Types over and over again. If you want to get up and running quickly, you can use this tool to add some defaults to Contentful. This runs the same script as Create new Content Type
under the hood, so will add the necessary local info to add each Content Type to the Metalsmith build.
The Content Types are:
Page
- static pagesPost
- regularly updated content like a blog postLink
- a link to internal or external content. Useful if you want to have multiple Links in a Series (e.g. an external link in your navigation menu etc)Series
- It's often useful to be able to group content into an ordered series, and this is easy with Contentful's powerful relational model. For example, the default navigation menu is aSeries
containing a list ofPages
andLinks
The default Content Types are all pretty basic, but are great for getting started. You can then run Add default content to Space
(see below) to populate the site with some dummy content
Assuming you've added the default Content Types (as above), you might also want to add some dummy content to see how the boilerplate works. It's also a good place to start if you want to repurpose some of it (e.g. it will create a Main Navigation Menu Series
which you can reuse with your own pages)
We love using Netlify to serve our static sites. It's really simple to set up a new site, and they handle continuous integration by linking to your Github repo.
Once you've created a new site and linked your repo, you'll need to tell Netlify how to build your site. This just means adding the build command and setting environment vars:
Branch - probably master
(the default), unless you're committing production code to a different branch
Dir - build
(default)
Build command - npm run build
You can add environment vars on the Advanced Settings tab. The ones you want are:
NODE_ENV
=> production
CONTENTFUL_SPACE
=> your_contentful_space_id
(get from .env
)
CONTENTFUL_DELIVERY_ACCESS_TOKEN
=> your_contentful_delivery_access_token
(get from .env
)
Click 'build your site'.
It's useful to get the Netlify site to rebuild every time you publish or unpublish content. To do this you'll need to create a new build hook in Netlify, then add it to your Contentful space
- Go to your Netlify site settings, and look for the Build Hooks pane under the Build and Deploy tab in the sidebar
- Generate a new build hook ('Contentful' is probably a good name...) and copy it
- Go to your Contentful space, and look for the Webhooks menu item under the Space settings dropdown menu
- Create a new webhook
- Don't worry about auth or headers
- Choose the events that will fire the webhook. For typical use, you'll just want to check the
Publish
andUnpublish
fields next to Entry.
(If you've already got the site building and you're happy re-using the same API key in development and production, you can copy these values from your .env
file. Otherwise generate a new API key in Contentful and copy the details from there. You won't be using the management tools on the server, so you won't need Contentful Preview or Management API keys)
Publish a change to an entry, wait for the site to build/deploy, and bask in the warm glow of how easy that all was.