Skip to main content

Building a snack review website with Eleventy and Contentful

Full disclosure: at time of writing, I’m employed by Netlify, and I work there because I’m a fan of the platform.

One thing I’d never quite gotten around to in my Jamstack journey was using a headless CMS for a personal project. I suppose this is because I’m quite content to hang out in a Markdown file. I decided the time was more than nigh to try one out, and so I decided to create a snack review site, using Contentful!

The snacks home page with a few items in view. The tagline reads: Eat snacks. Mostly candy. Almost too much.

World Snacks is a nice little excuse to try out international treats! All of this started when I was trying to track down a Ramune candy I’d had in Japan, and found I’ve since found a couple other neat sites, as well as some new-to-me local grocery stores to check out.

In any case, I grabbed my fave SSG (Eleventy), checked out this quick Eleventy + Contentful walkthrough, and got to hacking!

Content structure

Contentful enables you to create individual entries of particular content types within spaces (more on the data model), via a CMS dashboard. Data can then be fetched asynchronously, and rendered in the web project of your choosing.

On Contentful, I have a few different content types each with their own fields:

  • Snacks. These have “reference” fields pointing to relevant Country and Snack Type entries.
  • Countries
  • Snack Types
  • Pages (of which there is just one so far)

Integration with Eleventy

For most of the data above, integration with Eleventy goes a little something like this:

1. Create a global JavaScript data file

This data file initiates the Contentful JavaScript SDK client, supplying it with my Contentful space token and access token. Next, I call getEntries for whatever content I’m querying at the time. Let’s say it’s Countries. My data file returns an array of Countries and their fields from Contentful.

2. Generate country pages via pagination

In a country.njk file, I use pagination with a size of 1 to generate a page for each Country.

3. Render the country page

A country.njk layout file then prints data for this Country, e.g. country.countryName.

I also have a for loop that renders the snacks in this country, using the Snacks data that was similarly pulled from Contentful. I ended up creating an Eleventy filter (filterByCountry in my .eleventy.js file) in order to filter the full array of Snacks down to only those in the relevant Country. This takes the Country name, then checks the Snack’s Country reference field for a match. It’s technically possible to filter getEntries calls in a similar fashion, but I couldn’t figure out how to pass the Country name in as a variable to a Snacks data file…which I think makes sense given *handwaves* data lifecycles.

A page titled Spain with a review for spicy gummy mangos

Anyway, that’s about it! Data is fetched live from Contentful any time the site is built, either locally using my build-and-serve command (npm run start) or any time a new build is kicked off on Netlify. Which brings me to…

Building and deploy changes in Contentful to Netlify

When I was first putting together this site, I installed the Netlify app on Contentful, which enables you to manually trigger a build from the sidebar of the CMS. Funnily enough, I completely missed—until just now when writing this post—that there are simple auto-publish rules tucked a layer within the app.

Because I did want to automatically build and deploy newly published changes, I generated a Netlify build hook for my site, and registered it as a webhook on Contentful. Any time I (un)publish an entry or asset, a build kicks off on Netlify and grabs the latest data from the Contentful API (per the Eleventy integration I described). There are several other events to choose from as a trigger for webhooks.

Next I think I’ll filter triggers to particular content types. That way I can automatically deploy whenever I publish changes to a Snack, but not when I create a new Country; that could be empty until I “reference” it via a Snack.

I think I will keep using webhooks in lieu of auto-publishing with the app, because:

  1. There is far more granular control.
  2. The webhook logs let me view detailed information on when and why the webhook was triggered.
A list of times the webhook was fired, with a status code and 'view details' link for each

In general I would say Contentful’s user experience around webhooks is pretty nice. There’s a fair amount of detailed things you can do with them, but they’ve laid things out in such a way that make it pretty easy for me personally to digest:

Contentful webhooks settings. There's a grid of checkboxes for various different combinations of events, a filters section, and a headers section

Other Impressions on Contentful

In general, I found Contentful pretty easy to use, both from a CMS interface and headless API perspective. There are a couple minor things that are odd. For example, when viewing the “Snack” content type, the “Add” button is a dropdown, forcing me to take another click to add a new snack:

The add snack button just brings up a menu of options, from which you have to select add snack again

A split button might be more appropriate here.

The more meaningful gap I noticed is that Contentful doesn’t have a native concept of a “repeater” field. There is an app for that, but it’s limited to key-value pairs vs allowing you to repeat an arbitrary set of subfields (as Prismic’s group field does). Contentful’s play appears to be towards developer extensibility. Having built quite a few client websites in my day, though, I’d say a repeater field is worth building directly into the platform.

More fun, snacky things

To close out, I thought I’d share a few favorite details on this site.

For awhile now, I’ve been supporting both light and dark modes on websites, but always had that follow the user’s system. I finally got around to building in a theme switcher, in case you’d prefer to view this site in a different scheme than your system:

A theme switcher with system, light, and dark options The snack suggestion form rendered in light (cream, purple, and grey on white) and dark (various shades of dark purples)

A coworker says that the dark theme reminds them of black currant candy packaging, which I am delighted by given that black currant is one of the world’s top tier candy flavors (IMHO).

I also got way too much joy out of my tortilla chip pagination icons:

Country pagination that uses a tortilla chip icon for the arrows. A footer with a pattern of geometric cheese puffs.

And who doesn’t love a cheese puff pattern? I swear my time designing at Fuzzco has made me incapable of designing a subtle footer. There is just too much opportunity for one last burst of fun.

Check it out!

You can peek at the code on Github or check out the snacks. And I will always love to hear from anyone who has a snack suggestion. Happy snacking! 🥨


My blog uses Webmentions. Responses from sites which likewise support Webmentions, such as Twitter or people’s personal sites, will show up here.

No Webmentions have been sent to this post yet.