fix: notion value.value return

reference: https://github.com/NotionX/react-notion-x/issues/681
This commit is contained in:
Marc Seitz
2026-02-10 23:18:51 +11:00
parent 01b06b58e4
commit dcc47bba03
6 changed files with 93 additions and 5 deletions
+57
View File
@@ -43,6 +43,58 @@ function extractPageReferencesFromRichText(
return pageIds;
}
/**
* Normalizes the block entries in a recordMap to ensure a consistent structure.
*
* The Notion API (via `getBlocks` / newer API responses) sometimes returns blocks
* in a double-nested format:
* { spaceId, value: { value: { id, type, ... }, role: "reader" } }
*
* But `react-notion-x` expects:
* { value: { id, type, ... }, role: "reader" }
*
* This function flattens any double-nested entries so the renderer doesn't crash
* when calling `uuidToId` on an undefined block id.
*/
export function normalizeRecordMap(recordMap: ExtendedRecordMap): void {
// Normalize blocks
for (const blockId of Object.keys(recordMap.block)) {
const entry = recordMap.block[blockId] as any;
if (
entry?.value &&
typeof entry.value === "object" &&
entry.value.value &&
typeof entry.value.value === "object" &&
entry.value.value.id
) {
// Double-nested: flatten { value: { value: {...}, role }, spaceId } → { value: {...}, role }
recordMap.block[blockId] = {
value: entry.value.value,
role: entry.value.role ?? entry.role ?? "reader",
} as any;
}
}
// Normalize collections (same pattern can occur)
if (recordMap.collection) {
for (const collectionId of Object.keys(recordMap.collection)) {
const entry = recordMap.collection[collectionId] as any;
if (
entry?.value &&
typeof entry.value === "object" &&
entry.value.value &&
typeof entry.value.value === "object" &&
entry.value.value.id
) {
recordMap.collection[collectionId] = {
value: entry.value.value,
role: entry.value.role ?? entry.role ?? "reader",
} as any;
}
}
}
}
/**
* Fetches missing page blocks that are referenced in rich text but not in the recordMap.
* This fixes the issue where tables with multiple page links only show the first one.
@@ -50,6 +102,9 @@ function extractPageReferencesFromRichText(
export async function fetchMissingPageReferences(
recordMap: ExtendedRecordMap,
): Promise<void> {
// Normalize first so we can safely access block.value.properties
normalizeRecordMap(recordMap);
const allPageReferenceIds = new Set<string>();
// Iterate through all blocks to find page references in their properties
@@ -81,6 +136,8 @@ export async function fetchMissingPageReferences(
...newBlocks.recordMap.block,
};
}
// Normalize again after merging new blocks (getBlocks may return double-nested format)
normalizeRecordMap(recordMap);
} catch (err) {
console.warn("Failed to fetch missing page references:", err);
}
+8 -1
View File
@@ -1,7 +1,11 @@
import { NextApiRequest, NextApiResponse } from "next";
import notion from "@/lib/notion";
import { addSignedUrls, fetchMissingPageReferences } from "@/lib/notion/utils";
import {
addSignedUrls,
fetchMissingPageReferences,
normalizeRecordMap,
} from "@/lib/notion/utils";
import { log } from "@/lib/utils";
export default async function handle(
@@ -34,6 +38,9 @@ export default async function handle(
// Fetch missing page references that are embedded in rich text (e.g., table cells with multiple page links)
await fetchMissingPageReferences(recordMap);
// Normalize double-nested block structures from the Notion API
normalizeRecordMap(recordMap);
// TODO: separately sign the file urls until PR merged and published; ref: https://github.com/NotionX/react-notion-x/issues/580#issuecomment-2542823817
await addSignedUrls({ recordMap });
+7 -1
View File
@@ -13,7 +13,11 @@ import z from "zod";
import { fetchLinkDataById } from "@/lib/api/links/link-data";
import notion from "@/lib/notion";
import { addSignedUrls, fetchMissingPageReferences } from "@/lib/notion/utils";
import {
addSignedUrls,
fetchMissingPageReferences,
normalizeRecordMap,
} from "@/lib/notion/utils";
import { CustomUser, LinkWithDataroomDocument, NotionTheme } from "@/lib/types";
import LoadingSpinner from "@/components/ui/loading-spinner";
@@ -236,6 +240,8 @@ export async function getStaticProps(context: GetStaticPropsContext) {
recordMap = await notion.getPage(pageId, { signFileUrls: false });
// Fetch missing page references that are embedded in rich text (e.g., table cells with multiple page links)
await fetchMissingPageReferences(recordMap);
// Normalize double-nested block structures from the Notion API
normalizeRecordMap(recordMap);
// TODO: separately sign the file urls until PR merged and published; ref: https://github.com/NotionX/react-notion-x/issues/580#issuecomment-2542823817
await addSignedUrls({ recordMap });
}
+7 -1
View File
@@ -15,7 +15,11 @@ import z from "zod";
import { fetchLinkDataById } from "@/lib/api/links/link-data";
import { getFeatureFlags } from "@/lib/featureFlags";
import notion from "@/lib/notion";
import { addSignedUrls, fetchMissingPageReferences } from "@/lib/notion/utils";
import {
addSignedUrls,
fetchMissingPageReferences,
normalizeRecordMap,
} from "@/lib/notion/utils";
import {
CustomUser,
LinkWithDataroom,
@@ -153,6 +157,8 @@ export const getStaticProps = async (context: GetStaticPropsContext) => {
recordMap = await notion.getPage(pageId, { signFileUrls: false });
// Fetch missing page references that are embedded in rich text (e.g., table cells with multiple page links)
await fetchMissingPageReferences(recordMap);
// Normalize double-nested block structures from the Notion API
normalizeRecordMap(recordMap);
await addSignedUrls({ recordMap });
} catch (notionError) {
const message =
@@ -13,7 +13,11 @@ import z from "zod";
import { fetchLinkDataByDomainSlug } from "@/lib/api/links/link-data";
import notion from "@/lib/notion";
import { addSignedUrls, fetchMissingPageReferences } from "@/lib/notion/utils";
import {
addSignedUrls,
fetchMissingPageReferences,
normalizeRecordMap,
} from "@/lib/notion/utils";
import { CustomUser, LinkWithDataroomDocument, NotionTheme } from "@/lib/types";
import LoadingSpinner from "@/components/ui/loading-spinner";
@@ -246,6 +250,8 @@ export async function getStaticProps(context: GetStaticPropsContext) {
recordMap = await notion.getPage(pageId, { signFileUrls: false });
// Fetch missing page references that are embedded in rich text (e.g., table cells with multiple page links)
await fetchMissingPageReferences(recordMap);
// Normalize double-nested block structures from the Notion API
normalizeRecordMap(recordMap);
// TODO: separately sign the file urls until PR merged and published; ref: https://github.com/NotionX/react-notion-x/issues/580#issuecomment-2542823817
await addSignedUrls({ recordMap });
}
+7 -1
View File
@@ -15,7 +15,11 @@ import z from "zod";
import { fetchLinkDataByDomainSlug } from "@/lib/api/links/link-data";
import { getFeatureFlags } from "@/lib/featureFlags";
import notion from "@/lib/notion";
import { addSignedUrls, fetchMissingPageReferences } from "@/lib/notion/utils";
import {
addSignedUrls,
fetchMissingPageReferences,
normalizeRecordMap,
} from "@/lib/notion/utils";
import {
CustomUser,
LinkWithDataroom,
@@ -141,6 +145,8 @@ export const getStaticProps = async (context: GetStaticPropsContext) => {
recordMap = await notion.getPage(pageId, { signFileUrls: false });
// Fetch missing page references that are embedded in rich text (e.g., table cells with multiple page links)
await fetchMissingPageReferences(recordMap);
// Normalize double-nested block structures from the Notion API
normalizeRecordMap(recordMap);
await addSignedUrls({ recordMap });
}