splitbee / react-notion

A fast React renderer for Notion pages
https://react-notion.com
MIT License
2.86k stars 151 forks source link

Tracking react-notion vs react-notion-x #36

Closed transitive-bullshit closed 3 years ago

transitive-bullshit commented 3 years ago

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:

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 NPM docs Browser + SSR Fast React renderer for Notion.
notion-client NPM docs Server-side* Robust TypeScript client for the Notion API.
notion-types NPM docs Universal Core Notion TypeScript types.
notion-utils NPM 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

General Improvements

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 https://github.com/splitbee/react-notion/pull/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!

timolins commented 3 years ago

Wow, thanks for sharing all your hard work! A lot of great improvements have been made with react-notion-x.

Moving forwards I'd love to leverage a lot of the stuff you built. I outlined some of my thoughts on how to implement those changes, while remaining true to the vision of providing a minimal content renderer for Notion in #37.

Those are the things I think make sense to look into:

Lazy loading complex blocks is definitely a good idea. I would try to avoid depending on next/dynamic, since this rules out applications in other environments like Gatsby or manual SSR. I will put the new custom component system to the test, maybe it can provide a way to bring your own™ lazy load system.