splitbee / react-notion

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

Adds ability to override components from consuming app #30

Closed cball closed 3 years ago

cball commented 4 years ago

I am in the process of exporting our blog from Medium and wanted to make sure elements of our posts can be rendered by this library.

In doing so, we needed to render a tweet, but by adding it to this library it would include extra dependencies that aren't always needed. I'd like to propose adding a customComponents prop that takes mapping of block types and components. The components are called with the blockValue prop.

This way, react-notion can provide a base set of components, but everything can be overridden.

Let me know what you think!


Here's what the code looks like right now in the consuming app (using Next.js):

<NotionRenderer blockMap={blockData} customComponents={{ tweet: NotionBlockTweet }} />

And the component:

import React, { useState, useEffect } from 'react';
import Head from 'next/head';

/**
 * Renders a Tweet Block
 */
export const NotionBlockTweet = ({ blockValue }) => {
  const [url] = blockValue.properties.source[0];
  const [tweetHtml, setTweetHtml] = useState<string>();

  useEffect(() => {
    async function fetchTweet() {
      try {
        const response = await fetch(`/api/tweetHtml?url=${url}`);
        const json = await response.json();
        setTweetHtml(json.html);
      } catch (e) {
        // ignore
      }
    }

    fetchTweet();
  }, []);

  return tweetHtml ? (
    <>
      <Head>
        <script async src="https://platform.twitter.com/widgets.js" />
      </Head>

      <div
        className="notion-tweet"
        dangerouslySetInnerHTML={{
          __html: tweetHtml,
        }}
      />
    </>
  ) : (
    <div>Error loading tweet.</div>
  );
};
cball commented 4 years ago

Oops. @transitive-bullshit it looks like you've already started a version of this in #13. I'm not sure there's a ton of a benefit in a provider/consumer approach vs what I've suggested here, but I could be missing a use case you have.

timolins commented 3 years ago

Thanks for this PR! We plan to move this forward in the coming week, when we have some time to spend on react-notion.

Are there any downsides compared to the Context approach? Would love to hear @transitive-bullshit thoughts on this.

timolins commented 3 years ago

I think we can merge this now. @tobiaslins

It's now possible to overwrite decorator components as well. So full flexibility with this change 🙂

Here is an example that demos both decorator components as well as blocks. It adds target="_blank" to all text links, as well as wrapping page links with a Next.js link.

<NotionRenderer
  blockMap={blockMap}
  fullPage
  hideHeader
  customDecoratorComponents={{
    a: ({ decoratorValue, children }) => (
      <a href={decoratorValue} target="_blank" rel="noopener noreferrer">
        {children}
      </a>
    )
  }}
  customBlockComponents={{
    page: ({ blockValue, renderComponent }) => (
      <Link href={`/${blockValue.id}`}>{renderComponent()}</Link>
    )
  }}
/>