Description
React Notion X
Context
The purpose of this thread is to give some context between these two projects and track their differences, with the goal of ultimately converging on a single solution for the React Notion community going forwards.
I've spent the past few months working to improve react-notion
for Notion2Site. Towards that end, I've had several conversations with @tobiaslins and @timolins who did a lot of the amazing legwork in creating the first version of react-notion
.
I was working on a fork of react-notion
with all of my changes in a Next.js project (not a Git fork, unfortunately), and I finally found the time to move this fork back out into its own set of standalone packages: react-notion-x.
Summary of changes
The largest changes can be broken into a few themes:
- structural => isolating functionality into distinct packages
- new functionality => new blocks, solid collection support, dark mode, etc
- robustness => dozens of bug fixes
- testing => test suite covering as much notion functionality as possible
I'll go into a bit more depth on some of these below.
Packages
One of the biggest changes is structural. Instead of relying on a CF worker and having types and utilities duplicated between react-notion
and notion-api-worker
, all of this has been split into isolated packages:
Package | NPM | Docs | Environment | Description |
---|---|---|---|---|
react-notion-x | docs | Browser + SSR | Fast React renderer for Notion. | |
notion-client | docs | Server-side* | Robust TypeScript client for the Notion API. | |
notion-types | docs | Universal | Core Notion TypeScript types. | |
notion-utils | docs | Universal | Useful utilities for working with Notion data. |
imho it makes a lot more sense to expose a Notion API wrapper as notion-client
that can be used from Node.js (and Next.js SSR), Deno, etc instead of just CF workers.
The goal is that hopefully once Notion releases its public API, these packages should only require minor updates with react-notion-x
not having to change much if at all.
New Blocks
- table of contents
- checklists
- figma
- google maps
- google drive
- tweet
- audio
- file
- equation (block & inline)
- table collection view
- gallery collection view
- list collection view
- board collection view
- collection view page
General Improvements
- dark mode
- lazy loading heavy portions of the code via
next/dynamic
as a peer dep (eg, PDF support, collection support, equations / katex, etc) - support for LQIP images
- formula support
Performance
I know one of the things that Tobias and Timo expressed very fair concerns about has been how to manage the tradeoffs between react-notion
's original goal of being extremely lightweight and focused on embeddable use cases versus react-notion-x
's goal of faithfully supporting all Notion content with both embedded and full page use cases.
This is very closely related to perf and bundle size, two things which I've spent a lot of time working on with react-notion-x
, though there are definitely lots of tradeoffs to consider here.
I go into a bit more depth about my approach here, but in general I think that lazy loading larger optional dependencies like react-pdf
, react-katex
, as well as heavier portions of react-notion
like collection views and equation support, gets us the best of both worlds.
The first load JS and total JS bundle size for most pages is comparable with react-notion
, with larger optional dependencies only being loaded if needed by any given page.
The net result is that it's pretty easy to get solid lighthouse perf scores.
One downside of this approach is that I'm not sure how adding next/dynamic
as a peer dependency works for devs who may want to use React Notion from non-Next.js projects. Does anyone have any good examples of large third-party React libraries that support loading portions of their bundle dynamically (and work regardless of if the consuming app is using Webpack or another bundler)?
Design
react-notion-x
started as a fork of react-notion
, and although 90% of the code has changed, the core NotionRenderer
and Block
classes should look very similar.
I took a very similar approach to #30 via @cball in supporting the ability to override specific components via a components
prop on NotionRenderer
.
Conversely, I ended up finding that as the number of components and their complexity grew, it became significantly simpler to expose an internal-only NotionContext
that gives sub-components access to this configuration. It also became necessary for some interesting cases where when you render some subtrees, you want to completely negate the possibility of using <a>
tags even though that subtree could contain any type of blocks. You can see an example of this in CollectionCard which can contain lots of different types of property content that we want to ensure doesn't contain any links so we override components.link
and components.pageLink
with a dummy link component.
Closing Thoughts
I hope this gives a good overview of my changes to react-notion-x
with an emphasis on the most important design decisions.
I would've loved to have this be a real Git fork, but as I explained above this just wasn't in the cards for practical reasons as I learned about the scope of the changes needed as I went along.
Anyhow, I'm hoping that this public thread opens up a productive discussion that will benefit both projects and anyone interested in hacking on Notion in the future.
I'm very open to ideas about the best way to move both projects forward and hopefully merge them into a single react-notion
solution that will provide the most long-term benefits.
Thanks!