Import Notion pages into your Eleventy site.
To install the plugin package, use any of the following commands:
# For npm: npx jsr add @vrugtehagel/eleventy-from-notion # For yarn: yarn dlx jsr add @vrugtehagel/eleventy-from-notion # For pnpm: pnpm dlx jsr add @vrugtehagel/eleventy-from-notion # For deno: deno add jsr:@vrugtehagel/eleventy-from-notion
To get started with Notion and Eleventy, there's a few things to set up. Let's
start with the basic boilerplate for your Eleventy config file, and then walk
through the more important options to provide to the plugin. After having added
the plugin to your project, import it into your Eleventy configuration file
(usually .eleventy.js
) and add it using .addPlugin()
:
import EleventyFromNotion from "@vrugtehagel/eleventy-from-notion"; export default function (eleventyConfig) { // … eleventyConfig.addPlugin(EleventyFromNotion, { integrationSecret: process.env.NOTION_SECRET, database: "https://www.notion.so/0123456789abcdef0123456789abcdef", output: "notion/", schema: [ // … ], }); // … }
There's a few things going on here. First things first; our API key.
First, you'll need to create an internal Notion integration (which is what Notion calls their API keys). That sounds difficult, but don't worry; it's really just a few clicks. The process is described in detail in Notion's Create a notion integration documentation, but here's the gist of it:
At this point, you should be able to reveal and copy your "Internal Integration
Secret". Since this functions as an API key, which is a password of sorts, you
should not commit this to your version control software. Instead, configure an
environment variable, and set the integration secret using that. By default, if
you don't pass the integrationSecret
option directly, it is read from the
NOTION_INTEGRATION_SECRET
environment variable.
Next, we'll need to set up a database of pages to import. The integration does not automatically have access to your entire workspace, but instead, you need to "connect" the integration to a page, which then gives the integration access to said page and all pages under it. The plugin expects this "root" page to be a database, and each entry in the database should be a regular page (which will be imported). To add the integration to the database page, click the "•••" in the corner of the page and select "Connections", then find the integration you configured and select it. Then, in your Eleventy configuration, provide this database to the plugin, either as a full URL, or as ID (the latter being a UUID with or without dashes; this is contained within the URL).
Next, and this is the last thing we need to configure, is our "schema". The
pages you import are part of the specified database, and (can) have additional
properties beyond just their content. These are called "page properties" in
Notion, and the concept is very similar to "front matter" in Eleventy. However,
the structure and naming is naturally a bit different, and you might not want to
import every single page property anyway. So, you'll need to tell the
EleventyFromNotion
plugin exactly which properties to import, and what to
rename them to. For example, we probably want a page property to configure
permalinks. Let's say we called this field "Page URL" in Notion. We want it to
be imported as "permalink", so that Eleventy knows what URL to write the
template to. To configure this in the schema, we add an array entry like so:
eleventyConfig.addPlugin(EleventyFromNotion, { // … schema: [ { name: "Page URL", rename: "permalink" }, ], });
We can also configure nested front matter keys; for example, say we have a page
property in Notion called "SEO Description", then we can map this to the nested
seo.description
property by specifying name: "SEO Description"
together with
rename: ["seo", "description"]
. And, this brings me to the next point; "text"
page properties in Notion are always rich text. In Eleventy, you might not
always want rich text; for example, an SEO description will probably go into a
<meta>
tag, in which we want to use plain text. As such, text fields are
mapped to { rich, plain }
objects; for the SEO description example, that means
we can refer to it in our layout as {{ seo.description.plain }}
. The other
page property types map more directly to primitives, such as checkboxes mapping
to booleans, or "select" fields mapping to a string.
Lastly, a word about how the import process works. You may specify an output
path with the output
option, which defaults to notion/
. This is the path,
relative to your Eleventy input directory, that is used to write the imported
templates into. The file names match the page ID from Notion, i.e. they are
dashless UUIDs, and the extension is .html
by default (though this can be
configured to be something else, like .njk
). An additional .notion
file is
created in this directory, which contains a bit of JSON that the plugin uses to
keep track of its state between subsequent runs. If the directory used for
imports is not empty, and it doesn't contain the .notion
file, the import is
immediately aborted to avoid overwriting files unintentionally.
Notion pages are only imported if they have been updated since the last
successful import. In particular, this means that changes to the database
itself, such as the adding or removing of a column, does not reimport all the
pages. This is intentional; however, if you need to re-import all of your posts,
either delete the contents of the .notion
file (do not delete the file
itself!) or delete the output directory entirely.
For permalinks, there are two main recommended ways of setting things up. The
first option is for when you want to be able to decide the permalink from within
the comfort of Notion. To do this, configure a page property (i.e. database
column) with a type of "URL". This type is important; if you decide to use
"text", then the page property contains rich text, which generates a
{ rich, plain }
pair in the page's front matter. Eleventy then cannot process
your templates anymore because it expects a string for the permalink
. This
problem is avoided by using the "URL" type. Note that you must then also
configure your schema to map this page property to front matter; for example, if
the page property is named "Link" then add
{ name: "Link", rename: "permalink" }
to your schema
array.
The second way of setting up permalinks is through a computed property in
Eleventy. You are free to add files to the specified output directory (which
defaults to notion/
), as long as their names don't start with a valid Notion
ID (a UUID with or without dashes). For example, let's say you configured your
output directory to be output: "external/notion/"
. Then, you can add a file
external/notion/notion.11tydata.js
containing, for example:
export default { permalink: function (data) { // Assuming a rich text `title` field is configured: return `/blog/${this.slugify(data.title.plain)}/`; }, };
This way, each post generates its own permalink dynamically.
Add Package
deno add jsr:@vrugtehagel/eleventy-from-notion
Import symbol
import * as eleventy_from_notion from "@vrugtehagel/eleventy-from-notion";
---- OR ----
Import directly with a jsr specifier
import * as eleventy_from_notion from "jsr:@vrugtehagel/eleventy-from-notion";
Add Package
npx jsr add @vrugtehagel/eleventy-from-notion
Import symbol
import * as eleventy_from_notion from "@vrugtehagel/eleventy-from-notion";
Add Package
yarn dlx jsr add @vrugtehagel/eleventy-from-notion
Import symbol
import * as eleventy_from_notion from "@vrugtehagel/eleventy-from-notion";
Add Package
pnpm dlx jsr add @vrugtehagel/eleventy-from-notion
Import symbol
import * as eleventy_from_notion from "@vrugtehagel/eleventy-from-notion";
Add Package
bunx jsr add @vrugtehagel/eleventy-from-notion
Import symbol
import * as eleventy_from_notion from "@vrugtehagel/eleventy-from-notion";