NotionX / NotionX/react-notion-x

Next.js App Router Customer Image Component

Open
#549 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
5.4k
Forks
645
PR merge metrics
No merged PRs in 30d

Description

CleanShot 2024-04-17 at 09 13 15
I switch to AppRouter and using the "use client" for the NotionPage. The page rendered correctly but no matter what I try, the images are not optimized.
I both tried using the next/image, passing nextImage to the custom component prop
I also create a Cloudinary Image Component and pass to the customer component prop
It does not work.

Anyone have the similar issue?

Test Notion Page: https://www.notion.so/iant1359/About-Us-ea21588ce29b43038e42c7979d715b7d

My code:


// core styles shared by all of react-notion-x (required)
import 'react-notion-x/src/styles.css'

import * as React from 'react'
import dynamic from 'next/dynamic'
import Head from 'next/head'
import Image from 'next/image'
import Link from 'next/link'
// import { useRouter } from 'next/router'

import { ExtendedRecordMap } from 'notion-types'
import { getPageTitle } from 'notion-utils'
import { NotionRenderer } from 'react-notion-x'

import {CldImageProps} from "next-cloudinary"
import CldImage from './CldImage'

// import TweetEmbed from 'react-tweet-embed'

// -----------------------------------------------------------------------------
// dynamic imports for optional components
// -----------------------------------------------------------------------------

const Collection = dynamic(() =>
  import('react-notion-x/build/third-party/collection').then(m => m.Collection),
)

// Extend the props for your ImageComponent
interface ImageComponentProps extends CldImageProps {
  // Add any additional props here if needed
}

const ImageComponent: React.FC<ImageComponentProps> = (props) => (
  <CldImage
    {...props}
    width={2048}
    height={1600}
    alt="illustration image for doc"
    deliveryType="fetch"
    dpr="auto"
    config={{ cloud: { cloudName: 'iant1359' } }}
  />
);

 const NotionPage = ({
  recordMap,
  previewImagesEnabled,
  rootPageId,
  rootDomain,
}: {
  recordMap: ExtendedRecordMap
  previewImagesEnabled?: boolean
  rootPageId?: string
  rootDomain?: string
}) => {
  // const router = useRouter()

  // if (router.isFallback) {
  //   return <Loading />
  // }

  if (!recordMap) {
    return null
  }

  const title = getPageTitle(recordMap)

  // useful for debugging from the dev console
  if (typeof window !== 'undefined') {
    const keys = Object.keys(recordMap?.block || {})
    const block = recordMap?.block?.[keys[0]]?.value
    const g = window as any
    g.recordMap = recordMap
    g.block = block
  }

  const socialDescription = 'React Notion X Demo'
  const socialImage =
    'https://react-notion-x-demo.transitivebullsh.it/social.jpg'

  return (
    <>
      <Head>
        {socialDescription && (
          <>
            <meta name="description" content={socialDescription} />
            <meta property="og:description" content={socialDescription} />
            <meta name="twitter:description" content={socialDescription} />
          </>
        )}

        {socialImage ? (
          <>
            <meta name="twitter:card" content="summary_large_image" />
            <meta name="twitter:image" content={socialImage} />
            <meta property="og:image" content={socialImage} />
          </>
        ) : (
          <meta name="twitter:card" content="summary" />
        )}

        <title>{title}</title>
        <meta property="og:title" content={title} />
        <meta name="twitter:title" content={title} />
        <meta name="twitter:creator" content="@transitive_bs" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <NotionRenderer
        recordMap={recordMap}
        fullPage
        darkMode={false}
        rootDomain={rootDomain}
        rootPageId={rootPageId}
        previewImages={previewImagesEnabled}
        components={{
          // NOTE (transitive-bullshit 3/12/2023): I'm disabling next/image for this repo for now because the amount of traffic started costing me hundreds of dollars a month in Vercel image optimization costs. I'll probably re-enable it in the future if I can find a better solution.
          nextImage: ImageComponent,
          nextLink: Link,
          // Collection,
        }}

        // NOTE: custom images will only take effect if previewImages is true and
        // if the image has a valid preview image defined in recordMap.preview_images[src]
      />
    </>
  )
}

export default NotionPage
import { ExtendedRecordMap, SearchParams, SearchResults } from 'notion-types';

import { previewImagesEnabled } from './config';

// Type definition for the image map (if not already defined)
interface PreviewImageMap {
    [key: string]: string;
}

async function getPreviewImageMap(recordMap: ExtendedRecordMap): Promise<PreviewImageMap> {
    const previewImageMap: PreviewImageMap = {};
    const blocks = recordMap.block;
    
    if (blocks) {
        Object.keys(blocks).forEach(blockId => {
            const block = blocks[blockId].value;
            if (block && block.type === 'image' && block.properties && block.properties.source) {
                const imageUrl = block.properties.source[0][0] as string;  // Assuming source is stored this way
                previewImageMap[blockId] = transformImageURLToPreview(imageUrl);
            }
        });
    }

    return previewImageMap;
}

const notion = new NotionAPI();

export async function getPage(pageId: string): Promise<ExtendedRecordMap> {
    const recordMap = await notion.getPage(pageId);

    if (!recordMap) {
      throw new Error('Failed to retrieve the page data from Notion');
    }

    if (previewImagesEnabled && recordMap) {
      try {
        const previewImageMap = await getPreviewImageMap(recordMap);
        (recordMap as any).preview_images = previewImageMap;
      } catch (error) {
        console.error('Error fetching preview images:', error);
        throw new Error('Error processing preview images');
      }
    }

    console.log(recordMap)

    return recordMap;
}

export async function search(params: SearchParams): Promise<SearchResults> {
    return notion.search(params);
}

function transformImageURLToPreview(url: string): string {
    return url.replace('original', 'preview');
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the NotionRenderer custom nextImage component and the getPage path that populates recordMap.preview_images. Reproduce the provided Notion page with previewImages enabled, then trace whether ImageComponent receives the expected image source. Done means the reported App Router setup has a documented or verified image-optimization outcome.

Written by the indexing model from the issue text.

Assessment

Tech stack
next.js, react, typescript
Domain
frontend, web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.