npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@notion-headless-cms/react-renderer

v0.1.12

Published

Notion ブロックを React コンポーネントとして直接描画するレンダラ。shadcn/ui + Tailwind v4 ベース。

Readme

@notion-headless-cms/react-renderer

Notion API のブロックレスポンスを React コンポーネントとして直接描画するレンダラ。 notion-to-md / Markdown 経由ではなく、BlockObjectResponse を 1:1 で React ツリーに変換する。

UI プリミティブは shadcn/ui (new-york style) 由来、スタイルは Tailwind v4 ユーティリティクラスで完結する。

インストール

pnpm add @notion-headless-cms/react-renderer @notion-headless-cms/notion-orm @notionhq/client react react-dom

利用側プロジェクトに Tailwind v4 のセットアップが必須tailwind.config で本パッケージのソースをスキャン対象に含める:

// tailwind.config.ts (Tailwind v4 では @source を使う方式でも可)
export default {
  content: [
    "./src/**/*.{ts,tsx}",
    "./node_modules/@notion-headless-cms/react-renderer/dist/**/*.{js,mjs}",
  ],
};

使い方

import { Client } from "@notionhq/client";
import { fetchBlockTree } from "@notion-headless-cms/notion-orm";
import { NotionRenderer } from "@notion-headless-cms/react-renderer";

const client = new Client({ auth: process.env.NOTION_TOKEN });
const blocks = await fetchBlockTree(client, pageId);

export default function Page() {
  return <NotionRenderer blocks={blocks} />;
}

@notion-headless-cms/core と組み合わせて使う (推奨)

createClient 経由で取得すると、ブロックツリーが SWR キャッシュに乗り、画像 URL も cms.cacheImage 経由で永続プロキシ URL に書き換えられる (Notion 署名 URL の失効対策)。

import {
  type NotionBlock,
  NotionRenderer,
} from "@notion-headless-cms/react-renderer";
// "use client" を含まないサーバーサイド専用エントリ
import { resolveBlockImageUrls } from "@notion-headless-cms/react-renderer/server";

const post = await cms.posts.find(slug);
const notionBlocks =
  ((await post?.notionBlocks()) as NotionBlock[] | undefined) ?? [];
const blocks = await resolveBlockImageUrls(notionBlocks, cms.cacheImage);

return <NotionRenderer blocks={blocks} />;
  • cms.posts.find(slug).notionBlocks() — ブロックツリーをキャッシュ経由で取得 (DataSource.loadNotionBlocks を実装している場合のみ。@notion-headless-cms/notion-orm は対応済み)
  • resolveBlockImageUrls(blocks, cacheImage)image / video / audio / file / pdffile 型 URL を cacheImage(url) で書き換えた新しいツリーを返す。cacheImageundefined のときは入力をそのまま返す。external 型は触らない。children も再帰的に処理する
  • このサブパスは React Server Component やサーバーローダから呼び出すために "use client" を含めていない

コンポーネント差し替え

import { NotionRenderer, type ComponentOverrides } from "@notion-headless-cms/react-renderer";

const components: ComponentOverrides = {
  Code: MyCustomCode, // 既定の Code 実装を上書き
};

<NotionRenderer blocks={blocks} components={components} />;

数式 (KaTeX) を使う

v0.2 以降、block / inline equation は 既定で動的 import されるため、katex を peer に入れるだけで自動的に整形される(サブパス react-renderer/equation は廃止)。

pnpm add katex

利用側プロジェクトの CSS に katex/dist/katex.min.css を読み込むこと(Tailwind v4 なら @import "katex/dist/katex.min.css"@source より前に置く)。katex は optional peer なので数式を使わない構成では未インストールでよく、equation ブロックが現れた瞬間にだけ chunk が fetch される。

mermaid 図を使う(opt-in)

mermaid は約 1 MB と重く Cloudflare Workers の 3 MiB 上限を圧迫するため、既定では含めず opt-in サブパスで提供する。

pnpm add mermaid
import { NotionRenderer } from "@notion-headless-cms/react-renderer";
import { MermaidCode } from "@notion-headless-cms/react-renderer/mermaid";

<NotionRenderer blocks={blocks} components={{ Code: MermaidCode }} />;

MermaidCodelanguage === "mermaid" のときだけ動的 import で mermaid を読み SVG にレンダし、それ以外の言語は既定の Code に委譲する。

Notion 更新の表示反映 (/router, /next)

Notion のページを編集したあと、開いている画面を静かに最新化するためのフックとコンポーネント。クエリ無し・別 API への fetch 無しで、フレームワーク本来の再評価機構(loader / RSC)だけを使う。

React Router v7

import { NotionRevalidator } from "@notion-headless-cms/react-renderer/router";

export default function Post() {
  return (
    <article>
      <NotionRevalidator />
      {/* ... */}
    </article>
  );
}

内部で useRevalidator() を呼び loader を再走させる。サーバ側で cloudflarePreset({ env, ctx }) 等により waitUntil が配線されていれば、前回訪問時の SWR bg 更新で KV が最新化されており、再呼び出しで新内容が返って画面が差し替わる。

Next.js App Router

import { NotionRevalidator } from "@notion-headless-cms/react-renderer/next";

export default async function Page({ params }) {
  // ...
  return (
    <article>
      <NotionRevalidator />
      {/* ... */}
    </article>
  );
}

内部で useRouter().refresh() を呼び、Server Component を再評価して RSC ストリーム差分で UI を更新する。

オプション

| プロップ / オプション | 既定値 | 説明 | |---|---|---| | on | "mount" | 発火タイミング。"mount" / "visibility" / 配列で複数指定可。poll 指定時は既定が [](空)になる | | poll.url | — | peekVersion を返すエンドポイント URL | | poll.version | — | ページロード時の item.lastEditedTime(比較基準) | | poll.intervalMs | 500 | ポーリング間隔(ms) | | poll.timeoutMs | 30000 | タイムアウト(ms) |

<NotionRevalidator on={["mount", "visibility"]} />
// マウント時 + タブが visible に戻った時の両方で再評価

<NotionRevalidator
  poll={{ url: `/api/posts/${item.slug}/check`, version: item.lastEditedTime }}
/>
// KV ポーリング: バックグラウンド SWR 更新の完了を検出してから revalidate
// notionUpdatedAt 変化 → 即 revalidate / cachedAt 変化(確認完了・更新なし)→ 停止

フック版 useNotionRevalidate(opts) も同じシグネチャで提供。React 非依存の素 HTML 向けには @notion-headless-cms/core/htmlnotionRevalidatorScript() を使う。

対応ブロック

paragraph / heading_1-3 / bulleted_list_item / numbered_list_item / to_do / toggle / callout / quote / code / equation / divider / image / video / audio / file / pdf / bookmark / link_preview / link_to_page / child_page / child_database / embed / table / table_row / column_list / column / synced_block / breadcrumb / table_of_contents / unsupported

設計

  • 入力は fetchBlockTree が返す children を再帰解決済みのツリー
  • 全コンポーネントに "use client" ディレクティブが付き、Next.js App Router の server component から <NotionRenderer> を直接呼べる
  • 連続する bulleted_list_item / numbered_list_item は内部で <ul> / <ol> にグループ化される
  • interactive embed (Twitter widgets / YouTube facade) は副作用を hook で隔離