A Trello Board as a Lightweight CMS

Not every site needs a full-featured CMS. For straightforward, editable pages, there’s often a simpler path: using a tool your content editors already know. Trello, with its familiar card-based interface and a well-documented public API, is a surprisingly practical candidate for this job.

This article walks through a working example: a simple website that pulls its content from a Trello board. Each section of the page maps to a card on the board, with the card's title and description fields feeding the site's content. Since Trello supports Markdown, editors can apply basic formatting inside cards, and the build process renders it as HTML.

From Board to Browser: The Build Model

The approach relies on a build-time content pipeline. Instead of a server querying Trello on every page view, an automated build (the example runs on Netlify's CI/CD) fetches the board's data, applies it to a template, and static site generator (SSG) outputs the final HTML.

This "decoupled" pattern separates content management from presentation. Because content is pulled at build time, the site never carries the performance or quota risks of making live API calls under heavy traffic.

Exploring the Trello API

Trello’s API is well-documented, and there's an official Node client available on npm. But the easiest way to start exploring the data is a simple URL trick. Take the board URL:

https://trello.com/b/Zzc0USwZ/hellotrello

Appending .json returns the entire board's data as JSON. The same works for an individual card:

https://trello.com/c/YVxlSEzy/4-sections-from-cards

This JSON exposes useful metadata, including unique IDs for the board, lists, and cards, giving us the pointers needed to construct API queries.

Configuring the Content Source

A single column on the board is a natural fit for controlling the sections on a single page. Editors can add cards as new sections and drag them to reorder.

To get the data for those sections, we can take advantage of Eleventy's capability to use JavaScript in its data files. Instead of a separate fetch step, the SSG itself can request and process the data during each build.

All the required content is already present in the board's JSON, available in one request. We just need to filter it to a specific list's open cards, discarding items on other lists or that have been archived:

// trello.js
module.exports = () => {
   const TRELLO_JSON_URL='https://trello.com/b/Zzc0USwZ/hellotrello.json'
   const TRELLO_LIST_ID='5e98325d6d6bd120f2b7395f',
 
   // Use node-fetch to get the JSON data about this board
   const fetch = require('node-fetch');
   return fetch(TRELLO_JSON_URL)
   .then(res => res.json())
   .then(json => {
 
     // Just focus on the cards which are in the list we want
     // and do not have a closed status
     let contentCards = json.cards.filter(card => {
       return card.idList == TRELLO_LIST_ID && !card.closed;
     });
 
     return contentCards;
 });
};

Saving that filter logic in a trello.js file inside Eleventy's data directory exposes the structured content to templates via a global trello object.

Handling Image Attachments

We can also use file attachments on cards. If a card has an image, the code checks for it and appends standard Markdown image syntax to the card's description. This enriched content is then processed by Eleventy's Markdown engine into an HTML tag at build time.

// trello.js

// If a card has an attachment, add it as an image 
// in the description markdown
contentCards.forEach(card => {
  if(card.attachments.length) {
    card.desc = card.desc + `\n![${card.name}](${card.attachments[0].url} '${card.name}')`;
  }
});

Staging Content with Labels

Labels on cards provide a simple mechanism for staging. Instead of using separate boards or lists for draft and live content—which makes previewing new content in context difficult—we can tag cards with different labels.

The build process then filters content based on the branch it's running on. To make this work, the example project checks for a BRANCH environment variable, which corresponds to the git branch being deployed.

  • Cards labeled "live" appear on every build.
  • Cards with a label matching the BRANCH name appear only on that specific deploy.
// trello.js

// only include cards labelled with "live" or with
// the name of the branch we are in
contentCards = contentCards.filter(card => {
  return card.labels.filter(label => (
    label.name.toLowerCase() == 'live' ||
    label.name.toLowerCase() == BRANCH
   )).length;
 });

In practice, this means a stage branch deploy would include cards with the "stage" label. Since Netlify automatically creates branch deploys at subdomains like stage--yoursite.netlify.app, you get a full staging site for preview. Moving a card from "stage" to "live" is as simple as changing its label and triggering a new build.

Automating Updates with Webhooks

The CI/CD platform offers more than just branch previews. Netlify's build hooks are URLs that kick off a new deployment when they receive an HTTP POST request. Trello also supports webhooks, so we can automatically trigger a rebuild whenever the board changes.

After creating a build hook in a site's admin panel:

Screenshot of the netlify build hooks screen with options to add a build hook and generate a public deploy key.
Creating a Netlify Build hook

...the remaining setup is to register that URL as a Trello webhook via their API. The example repo includes a utility for this, but you'll first need to obtain a developer key and token from the Trello Developer portal. Saving those credentials in a local .env file allows you to run a single command to register the webhook:

npm run hook --url https://api.netlify.com/build_hooks/XXXXX

With that configuration, the site now has automatic, event-driven updates.

Is This Right for Your Project?

This small example is deliberately simple, and it demonstrates core concepts of decoupling and building with external data sources. While it won't replace a full-featured CMS for complex projects, the underlying model works across various project sizes.

It's well-suited to a set of common business web pages: a restaurant's site might use one list for the homepage content and another for daily specials, giving staff an intuitive board for edits that avoids fiddling with files or uploading new PDFs.

You can explore a working example to see the pieces in action:

For more details, dig into the Trello developer resources or Netlify's guidance on branch deploys.