🔄 Yet another way to establish Notion + Jekyll synchronization
Hello there. Jekyll is a wonderful tool for building static websites. I like it a lot, but it lacks some kind of CMS. I would love to be able to write posts anywhere, but out-of-the-box, the only way to write something is via git. This method restricts you heavily:
-
It is quite difficult to set up a comfortable workspace on mobile devices, so the only way to write is to use a desktop.
-
Even on a desktop, you need to configure it enough to be able to write posts. I have several desktops, and not every machine is dev-configured. You have to share SSH keys, install Git, install your favorite editor, and so on.
Existing “CMS” solutions
There are some popular solutions, such as jekyll-admin, prose.io, and others. Honestly, I haven’t even tested them on a real website because all of them lack ability to provide comfortable editing on a mobile. They usually provide a web UI, and you have to login there to edit your content. I’m not sure about other features, such as seamless synchronization, drafts, and configuration. Maybe they are okay, but still, in-browser editing on a mobile is a fatal drawback for me.
Notion
Notion comes to the rescue. Just imagine being able to manage your Jekyll-powered website with Notion:
-
First-class mobile application and web UI.
-
It’s free.
-
Everything is synchronized out-of-the-box, so you can start writing on mobile and immediately switch to a desktop to continue.
-
And many other features. You probably already know what Notion can do 🌚.
Initially, it may sound difficult to integrate Notion with Jekyll, but it’s actually not (at least after reading this article). There are many tutorials about “how to configure Notion + Jekyll sync”, and there are many ways to do it.
So, Notion + Jekyll
I have come across various approaches on how to do it, and most of them follow a similar pattern: pack the logic (connect to Notion API, access the database, parse content into markdown, etc) into a Docker container, run it using a cron job, and git-commit the output.
In general, I like this approach, but I don’t like the need to manage a large amount of logic, especially if it’s written in a language other than Ruby. Ideally, I want everything to be bundled as a Jekyll plugin with simple configuration via _config.yml
.
Luckily, I found a solution that almost meets my requirements: emoriarty/jekyll-notion. The only thing that it does “wrong” for me is that it doesn’t store all the content under git control; it syncs only during the build stage every time. Well, this approach mostly works okay, but there are some drawbacks:
-
It depends on Notion API availability and internet connectivity during the build.
-
You can’t manually edit Notion-powered pages (e.g., if something exported wrong).
-
It ties your website too much to Notion. If you decide to move to some other CMS, you will need to migrate all your content manually.
-
If you accidentally remove your Notion database, you will lose all your content.
jekyll-notion prefetch_mode
That’s why I implemented prefetch_mode for this plugin. It introduces the new command, jekyll prefetch
, which pulls and converts your Notion content according to _config.yml
and places it in the appropriate source directory. All you have to do after that is git commit
and git push
to trigger the build. This mode still lacks some features, such as asset fetching, custom page fetching, and plain data fetching, but it’s a good start nonetheless.
My final setup
So, to configure sync, follow these steps:
-
Create a Notion database by following this guide. If you’re unsure what a database is, read this article.
-
Create a new connection by going to My Integrations. Then copy the given secret.
-
Assign the newly created connection with the database.
-
Go to your website repository and edit the following files:
-
Gemfile
:
# ...
group :jekyll_plugins do
# ...
# github.com/emoriarty/jekyll-notion
gem 'jekyll-notion', '2.3.3'
# note: as of 25.08.23 pull request is not merged yet,
# so to access the changes, you can use my fork like this:
# gem 'jekyll-notion', '2.3.2', :git => 'https://github.com/seroperson/jekyll-notion.git'
end
_config.yml
:
# _config.yml
# ...
# set default layout for your posts
# make sure it set because otherwise your notion-powered
# posts will look weird
defaults:
- scope:
path: ""
type: posts
values:
layout: post
sitemap: true
hidden: false
# ...
notion:
# disables the sync at build stage
# makes `jekyll prefetch` command available
prefetch_mode: true
databases:
# your database id
# https://www.notion.so/{workspace_name}/{database_id}?v={view_id}
- id: 123abc
# filter based on your database properties'
# replace with your own
filter: { "property": "Status", "select": { "equals": "published" } }
- Configure new CI job in
.gitlab-ci.yml
(or follow the guide of how to do it according to CI you use):
# $BOT_NAME $BOT_EMAIL $TOKEN_NAME $ACCESS_TOKEN $NOTION_TOKEN
# variables must be defined
# ...
pages:
stage: deploy
# ...
notion-sync:
stage: deploy
rules:
# run this job just only if it was scheduled or triggered manually
- if: ($CI_PIPELINE_SOURCE == "manual" || $CI_PIPELINE_SOURCE == "schedule") && $NOTION_TOKEN != null && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- |-
# run our synchronization command
bundle exec jekyll prefetch
# if there are any changes from notion ...
# note: be sure that all files produced by
# (i.e. bundle install) are git-ignored
if [[ $(git status --porcelain) ]]; then
git config user.name $BOT_NAME
git config user.email $BOT_EMAIL
git add .
git commit -m "Notion sync $(date +"%Y-%m-%d")"
git remote add ci "https://$TOKEN_NAME:$ACCESS_TOKEN@gitlab.com/<username>/<your-repo>.git"
git push ci HEAD:$CI_COMMIT_REF_NAME
fi
- Finally, schedule a build via GitLab UI
Make sure all necessary environment variables are set.
That’s it! Now your Jekyll website is Notion-synchronized. This post is the first one written with such synchronization. Stay tuned for updates on my Telegram channel 🛫 or subscribe via RSS. Have a nice day!