Ben Borgers

How to turn a Notion doc into a website

April 18, 2020

Heads up! This post was written before Notion came out with an official API, and uses a reverse-engineering method that isnā€™t officially supported. Notion now has an official developer API, so you should use that instead. I no longer maintain Potion, which is used in this article.


I wrote my own reverse-engineered API for Notion, called Potion. The code is open source on GitHub.

Today, weā€™re going to use that API to turn a Notion document into a website.

Thereā€™s two ways of doing it: using a static website (no server), or using a Node.js server. Weā€™ll also discuss the pros and cons of each.

Getting the ID of your Notion doc

For both of these methods, youā€™ll need the ID of your Notion doc.

First, make the Notion doc public using the Share button in the top right corner:

Then, click the Copy page link button and paste it somewhere. The long random string of characters in that link is the document ID:

Weā€™ll use this ID in our code later.

Method 1: Static website

The benefits of this approach are that you can host the website very cheaply (for example, on Netlify) since itā€™s just static files. However, the webpage is empty before the javascript executes, and crawlers (like Google) need to run the javascript in order to ā€œseeā€ the contents.

Letā€™s set up the skeleton of the web page:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Notion Doc</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="/style.css" />
    <script src="/script.js" defer></script>
  </head>
  <body>
    <main></main>
  </body>
</html>

This empty HTML page links a javascript file (at /script.js) and a CSS file (/style.css).

I also added an empty <main> element in the body, which is where weā€™ll load the Notion doc into.

Then, I wrote some javascript in script.js that makes a request to the Potion API and fills in the <main> element with the result. It uses the Notion document ID you copied earlier.

const notionDocId = "0cb628857f3c4c77bf7f9a879a6ec21d";

fetch("https://potion-api.now.sh/html?id=" + notionDocId)
  .then((res) => res.text())
  .then((text) => {
    document.querySelector("main").innerHTML = text;
  });

Thatā€™s it! Now you have a website powered by your Notion doc. Hereā€™s the full code if youā€™d like to check it out: Live demo and source code.

Method 2: Node.js server

The benefits of this approach are that the HTML sent by the server fully includes the Notion documentā€™s content, so itā€™s easy for web crawlers (like Google) to read and understand. It doesnā€™t require any client-side javascript to run. However, you need to run a full Node.js server to host it, so this solution canā€™t be hosted on a static file host.

I started by installing express, a framework for Node.js servers, and node-fetch, a package that replicates the fetch API in Node.js.

const express = require("express");
const app = express();

const fetch = require("node-fetch");

app.listen(process.env.PORT);

Then, I added this Express route. It uses the Notion doc ID you copied earlier, makes a request to the Potion API, and then inserts the result of the API request in an HTML document on the server.

app.get("/", (req, res) => {
  const notionDocId = "0cb628857f3c4c77bf7f9a879a6ec21d";

  fetch("https://potion-api.now.sh/html?id=" + notionDocId)
    .then((res) => res.text())
    .then((text) => {
      res.send(`
        <!DOCTYPE html>
        <html>
          <head>
            <title>Notion Doc</title>
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <style>
              body {
                font-family: system-ui, sans-serif;
              }

              img {
                max-width: 100%;
                max-height: 70vh;
              }

              /* add your own CSS to make it look how you want */
            </style>
          </head>
          <body>
            <main>${text}</main>
          </body>
        </html>
      `);
    });
});

This website looks the exact same, but is generated on the server-side instead of in the browser. Hereā€™s the full code for the Node.js solution: Live demo and source code.