🔄 Yet another way to establish Notion + Jekyll synchronization
Jekyll is a wonderful tool for building static websites. It has its’ own pros and cons. One of the drawbacks of many website generators is the lack of a 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 way 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.
- 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.
- If your website’s source code is open-sourced, then it will be incomplete: all the Notion content will be missing.
jekyll-fetch-notion
That’s why seroperson/jekyll-fetch-notion was born (as a result of a pull-request). It’s a fork of jekyll-notion
, aimed at synchronizing things separately from the build phase. It introduces a new command, jekyll fetch_notion
, 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 plugin still lacks some features, such as 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/seroperson/jekyll-fetch-notion
gem 'jekyll-fetch-notion'
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
fetch_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" } }
- (optionally) Configure an additional CI job in
.gitlab-ci.yml
(or follow the guide of how to do it according to CI you use), so you can trigger synchronization just by pressing a button:
# $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 fetch_notion
# 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
- (optionally) Schedule a build via GitLab UI
Make sure all necessary environment variables are set.
Conclusion
Thank you for reading this little article, I hope it will be useful to someone. Feel free to reach me if you have something to say.
And also take a look on the posts on similar topics: