A roots extension to work with data from Contentful's API. https://www.contentful.com/
CoffeeScript JavaScript Makefile
Latest commit fb48cac Sep 27, 2016 @jescalan jescalan committed on GitHub update chai-as-promised to version 6.0.0 (#158)


Roots Contentful

Gitter Chat

npm tests dependencies devDependencies Coverage Status

An extension for using roots with the Contentful CMS API.

Note: This project is in early development, and versioning is a little different. Read this for more details.

Why Should You Care?

We love static sites. They're fast, resilient, simple, and cheap.

However, managing and updating content on a static site is a pain. This extension allows you to load data from Contentful's API into your roots project for use in your view templates during compilation. Non-developers get an easy way to publish and manage content, while developers can still build static sites in a sane way.


  • make sure you are in your roots project directory
  • npm install roots-contentful --save
  • modify your app.coffee file to include the extension, as such
contentful = require 'roots-contentful'

# ...

module.exports =
  extensions: [
      access_token: 'YOUR_ACCESS_TOKEN'
      space_id: 'xxxxxx'
          id: 'xxxxxx'
          template: 'views/_post.jade'
          filters: { 'fields.environment[in]': ['staging', 'production'] }
          path: (e) -> "blogging/#{e.category}/#{slugify(e.title)}"
          write: 'data.json'
          sort: compareFunction
          transform: transformFunction
          id: 'xxxxxx'

# ...


Accessing Content in Views

A contentful view helper object will be passed into every view containing your content. Each content type will be set as a property on contentful using the name option in your app.coffee configuration. For example with the app.coffee file above, you can access the blog posts like this:

  h1 Hello World
    - for post in contentful.posts
        h2= post.title
        p= markdown(post.body)

Note: for the above markdown function to parse your post.body, you need to install a markdown parser for roots to work with. Using marked as an example, do the following:

  • npm install marked --save

And then in app.coffee:

contentful = require 'roots-contentful'
marked     = require 'marked'

  markdown: marked

See the roots documentation for more details.

Note: if you have Links in your content more than 10 levels deep (the max for the include parameter), then unresolved links can be returned.

Single Entry Views

If a template option is defined for a Content Type in app.coffee, roots will compile a single page view for each entry in that Content Type collection. The entry will also have a _url key that returns the path to the single page view (so you can create links on an index page for example).

The Entry Object

Contentful's documentation shows the API response when fetching an entry. Your content fields are nested in a fields key on the entry object. As a convenience, the entry object roots-contentful makes available in your views will have the fields key's value set one level higher on the object. System metadata remains accessible on the sys key and roots-contentful will raise an error if you have a field named sys. Inside your views, the entry object will have this structure:

"entry": {
  "title": "Wow. Such title. Much viral",
  "author": "The Doge of Venice"
  # ... the rest of the fields
  "sys": {
    "type": "Entry",
    "id": "cat"
    # ...

And can be accessed in your view like this:

  h2= entry.title
  p= markdown(entry.body)
  if entry.image
    img(src!= asset(entry.image)

Configuration Options


Required. Your Contentful Delivery access token (API key).


Required. The space ID containing the content you wish to retrieve.


Optional. (Boolean) Allows you use the Contentful Preview API. Also able to be accessed by setting the environment variable CONTENTFUL_ENV to "develop" (preview api) or "production" (default cdn).


An object whose key-value pairs correspond to a Contentful Content Types. Each content type's entries will be set on the contentful locals object using the key used in the config.

Configuring a content_type

Each object in the content_types array can have the following properties:


Required. The Content Type's ID on Contentful.


Optional. This is the name of the key the entries will be attached to on the contentful object in your views. Defaults to a pluralized, underscored representation of the Content Type name (e.g. 'Blog Post' => contentful.blog_posts)


Optional. Path relative to the roots project of a template for a single entry view. Each entry in the Content Type will be passed into the template in an entry variable. If not given, the Content Type will not be compiled into single entry views and will only be attached to the contentful view helper object.


Optional. Takes an object with different search and filter criteria. See examples of how to structure the object in Contentful's docs.

For example, the following limits the API response to 5 entries, filters by the feature field and orders items in the response by the sys.createdAt property. Notice the minus sign, which reverses the sort order.

    id: 'xxxxxx'
    template: 'views/_post.jade'
    path: (entry) -> "blog/#{entry.permalink}"
    filters: {
               'limit': '5',
               'fields.feature': 'true',
               'order': '-sys.createdAt'
    transform: transformFunction


Optional. Provide a function that returns a string of the relative path to the output file for a given entry without the extension. First argument passed into the function is the entry. Default is <name>/<slug> where slug is the slugified output of the entry's displayField (a property of the Content Type), and name is the provided name option above or the default value. This option is ignored if no template is given.

If the function returns an array of paths, the given entry is compiled to multiple files. The array of paths can be accessed with entry._urls. For instance, the configuration below outputs each blog post entry into two folders, en and fr, for i18n purposes:

    id: 'xxxxxx'
    template: 'views/_post.jade'
    path: (e) -> ("#{lang}/#{slugify(e.title)}" for lang in ['en', 'fr'])


Optional. Provide the relative path to the output file that will hold the JSON data of the current content type. The top level JSON object will be an array.


Optional. Provide a function to transform (map) every entry in a content type. The transformed data is then added to the jade locals variable and written to JSON (if the write property is provided). Transform can also return a when.js promise.

transformFunction = (entry) ->
  delete entry.myProperty


Optional. Provide a standard compare function that will sort all the data of a content type. The data is sorted after it is transformed and before it is provided to the jade locals variable or written as JSON.

compareFunction = (a, b) ->
  # 0           => a and b are equal
  # -1 or less  => a is before b
  # 1 or more   => a is after b
  a.number - b.number

Asset Image Helper

roots-contentful also provides a convenient view helper called asset that allows you to pass in the asset object returned from their API and returns the url. You can also pass in options that will be appended to the image url as a a query string that allows you to specificy size and quality params as documented here.

- for post in contentful.posts
  img(src!= asset(post.image, {w: 100, h: 100, q: 50}))

License & Contributing