1. Switching between Confluence and Notion can be difficult as both have their unique way of presenting content. It's crucial to create a bridge between these two worlds where less time will be spent on context switching and more on what matters. Thus one of our primary inspirations was to give users access to their notion content all from the comfort of their confluence page.

  2. It's not an easy task to learn the basics of Notion without dedicating a considerable amount of time. If anybody in the team wants to work in Notion, they will introduce friction in the workflow as the other team members would also have to create an account and learn at least the basics. But this macro eliminates this step. By making a notion page public, anybody having access to the macro can view the content of that page without even creating an account.

  3. As mentioned earlier notion and confluence are two vastly different worlds, each with its unique strengths. In a word, they are not compatible with each other. Viewing the same content in both apps and keeping them in sync could prove to be extremely cumbersome if not outright impossible. Plus it's always better to keep a single source of truth for your content. Taking advantage of Notion's rich ecosystem of blocks, without having to worry about keeping things in sync and replicating them was another significant inspiration behind the creation of this macro.

We have previously built

What it does

Notion For Confluence is an app built using the Forge platform, which can render a Notion page inside of Confluence given its link. By providing auth credentials it can also generate private pages. From backlinks to both page/block level comments, inline dates, mentions, and so on, it supports all the basic notion blocks along with several advanced blocks, and support for more types of blocks are coming up. Please note that at the moment this is read-only, as this app can't edit/update the contents of the Notion page.

How we built it

Our language of choice was Javascript or Typescript more specifically. Both the client and server-side code takes advantage of @nishans/types. It's an npm package that I've created for the sole purpose to provide typescript type definitions for Notion.


  1. React (UI Library)
  2. Snowpack (Bundler)


  1. Forge Storage API
  2. Unofficial Notion API

How does it work


The main tasks of the frontend are to:-

  1. Fetch the block data from the backend API.
  2. Render the fetched data in the UI.

To render a page, the user of the app must provide the link of the notion page or just its id. After that as soon as they click on the Render button, the following things happen:-

  1. The client sends an API request to the backend for the initial page data
  2. The client builds a network of related blocks that are connected to the fetched page data, which needs to be fetched from the server.
  3. Once the client has all the connected data, it starts the rendering phase

At the moment the connected data consists of:

  1. Page aliases (External embedded page links)
  2. Collection
  3. Notion users that have access to the block and the space
  4. Block level and page level comments
  5. Backlinks
  6. Nested Pages


Currently, this app doesn't make use of Notion's public API. Even though the public API has a lot of abstraction to cover away from the intricacies and complexity of the data, we had no choice but to revert to the raw API simply because of enriched functionalities. For authenticated requests, Notion internally uses a token that could be generated as a product of an auth flow. Right now we only support authentication via basic email/password. Once the token is generated on the backend an encrypted version of it is stored in Forge Storage. For all authenticated requests it uses the token after retrieving it from the Storage.

Challenges we ran into

1. Making sense of the data

The first challenge was to make sense of the data that notion's raw API provided. Notion consists of several types of blocks, and a lot of them are interconnected and dependent upon one another. We needed to figure out which data the root block (page/database) required to render properly. The process required a lot of trial and error. For this purpose, we used tools like Postman, and Chrome Devtools along with packages like Axios. After a lot of inspection, the shape of the data along with its dependencies began to make sense.

2. Rendering the data

Forming a connection between the data and the UI was a big challenge. We had to spend a lot of time trying to emulate the look and feel of the actual notion app, but due to unfamiliarity and the complexities of the app's design system, a lot is still left to be achieved.

3. OAuth Sign-in

Unfortunately, even after several attempts, we were unable to integrate OAuth sign-in into the app. A feature that is quite simple to implement in a web app turned out to be a bit more complicated due to how notion performs it in the actual app.

4. Forge's timeout limitation

Since we had to rely on Notion's raw API, most of the time a response didn't fetch the right contents. Thus it required us to make further requests to get fetch all the data. As a result during the development phase, we faced a lot of timeout issues due to the server taking too much time to compute and return a response.

Accomplishments that we're proud of

Even though there were some initial hurdles, we were able to accomplish some impressive feats. A few of them are mentioned below:-

  1. Notion provides a whole bunch of blocks and comprises a complex system of interconnected components. So being able to make sense of the data and how they are related was one of the biggest accomplishments.
  2. We are aware that the current mechanism of email/password combination is suboptimal. But one of our biggest priorities was to give authenticated users full access to their private content.
  3. Notion's design system is complex, and the CSS styles inspected from the chrome dev tools didn't help our cause that much. But we managed to render most of the basic blocks up to a few of the more complex/advanced ones.
  4. Being able to overcome the timeout limitation that forge enforces on its lambdas by splitting computations (mostly consisting of API requests) across several lambdas and stitch them together in the frontend.

What we learned

  1. We were able to get a glimpse of how Notion's backend works internally. So even if we switch to the public API in the future, we can deliver updates at a faster rate, by integrating the private API where ever necessary.
  2. React alongside Typescript is probably one of the best combinations to create the client-side of a forge app in our opinion. Additionally adding a fast bundler like Snowpack sped up our frontend development.
  3. It's better to split out related and connected computations into several lambdas due to Forge's time limit limitation. This will result in a bit more delay on the client-side as several chunks of requests are being made rather than one giant one but with the right approach like a custom loader, the UX can be vastly improved.

What's next for Notion for Confluence

This is just the start for Notion for Confluence. We have plans to add support for almost all the native read-only features that notion's client provides, starting by targeting more complex blocks. We also want to make the auth process a lot easier than it's now at currently by introducing google OAuth, and maybe soon add the ability to modify the page as well all from the comfort of this app.

Built With

Share this project: