Back Original

A SQLite Plugin for Jekyll

06 May 2026

Over the last decade, I’ve made a bunch of sites using some mix of data/content. The most popular of these is endoflife.date, but there’s also hackercouch.com, mf.captnemo.in and a bunch more.

My preferred stack for such projects is Jekyll, now with custom plugins since GitHub Pages now runs on GitHub Actions with custom plugins by default.

Simon Wilson’s “Baked Data” pattern is something I’ve started identifying across my projects, where I’d often push my dataset as a CSV file, and render the website by using the data directly using Jekyll.

Jekyll supports YAML, JSON, CSV, and TSV files by default, which makes your “baked data” quite inflexible - writing anything complex - say you want a page for “Berlin’s top rated restaurants”, would not look so nice in Liquid


{% for r in site.data.restaurants | where:"rating>4"%}
{% location = r.address | downcase %}
{% if "berlin" in r.name %}
<td>{{r.name}}</td>
{% endif %}
{% endfor %}

If you want your pages to have any level of dynamism - which requires re-shaping your data from CSV files, it gets too complicated very soon. Doing joins in Jekyll across 3 CSV files is Not Fun(tm).

Jekyll-SQLite

So I built jekyll-sqlite: A Jekyll plugin that lets you use sqlite databases as your data source. It lets you write this query in your Jekyll config file:

sqlite:
  data: berlin
  file: restaurants.db
  query: "SELECT * from restaurants WHERE rating>4 AND location LIKE '%berlin%'"

And use the resulting data via site.data.berlin.

This is pretty helpful out of the box, but it gets pretty amazing if you combine a few more features:

Collections

If you’d like to treat the output of your query as a jekyll collection, you can do that as well:

sqlite:
  collection: restaurants
  file: restaurants.db
  query: "SELECT title,description as content from restaurants

You can configure this collection like any other in your Jekyll config (set a layout/permalink for it for eg).

Jekyll Datapages

As an alternative to collections, you can also use jekyll-datapages - a plugin that lets you generate pages on the fly for each entry in an array that comes from site.data. This lets you write a query and generate pages for every row of the result set.

So you can have a SQLite query: SELECT * FROM venues, and use this plugin to generate a page per venue, with the data from each record available inside page.venue.

Per-Page queries

Per-Page queries in the jekyll-sqlite plugin. You can write queries that run on a per-page basis. So a page called berlin.md could have a query that defines page.restaurants via the SQL Query ‘SELECT * FROM restaurants WHERE rating>4 AND location LIKE `%Berlin%’.

Parameter Binding

It supports parameter binding for your SQL Queries, and the parameters can come from the page front-matter, or the site-level configuration. So you can write the following:

location: Berlin
sqlite:
  - data: restaurants
    file: r.db
    query: SELECT * FROM restaurants WHERE location LIKE '%' || :location '%'
  - data: top
    file: r.db
    query: SELECT * FROM events WHERE location LIKE '%' || :location || '%' AND rating>4

---
A page about Berlin's restaurants.

:location gets bound via page.location (from the first line) = "Berlin".

Jekyll Defaults

Jekyll lets you defined defaults on the entire collection, so if all your location pages are inside locations/*.md, you can add the following configuration:

defaults:
  - path: "locations/*.md"
    values:
      layout: location-page
      sqlite:
      - data: restaurants
      file: r.db
      query: SELECT * FROM restaurants WHERE location LIKE '%' || :location '%'
    - data: top
      file: r.db
      query: SELECT * FROM events WHERE location LIKE '%' || :location || '%' AND rating>4

Now you only need to specify your location in the front-matter in Berlin.md:

location: Berlin
---
Page about Berlin's restaurants.

and this page will be rendered with page.restaurants, page.top variables using the location-page layout. You can now create a new page for every city you track, and all they need is just a location attribute.

Reference

Fun Fact: The first version of this was brainstormed and built during Recurse Center’s Never Graduate Week celebrations in Bangalore. I’m just publishing this 2 years late.

Published on May 06, 2026