notionic 0.0.0-stage → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +135 -0
- package/LICENSE +21 -0
- package/README.md +261 -2
- package/dist/fetch.d.ts +62 -0
- package/dist/fetch.js +259 -0
- package/dist/files-DIc0m6LM.js +104 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.js +1266 -0
- package/dist/markdown.d.ts +15 -0
- package/dist/markdown.js +162 -0
- package/dist/page-BmeweSIt.js +27 -0
- package/dist/styles.css +108 -0
- package/dist/types-DOxr5KpT.d.ts +1305 -0
- package/dist/utils-BL_oAR33.js +18 -0
- package/package.json +73 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# notionic
|
|
2
|
+
|
|
3
|
+
## 0.8.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#42](https://github.com/nublson/notionic/pull/42) [`7d202c2`](https://github.com/nublson/notionic/commit/7d202c2bd121d6d6ca94cd0f951ee6ffb72f8cdd) Thanks [@nublson](https://github.com/nublson)! - Adds npm search keywords (`notion-api`, `notion-renderer`, `nextjs`) and a link to GitHub issues to the package metadata.
|
|
8
|
+
|
|
9
|
+
## 0.8.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#39](https://github.com/nublson/notionic/pull/39) [`2d6256c`](https://github.com/nublson/notionic/commit/2d6256c46055aa99c61e7e786a7c3dd03542cad1) Thanks [@nublson](https://github.com/nublson)! - Inline code of up to 24 characters no longer breaks across lines, so short names like `notion-callout` stay whole instead of splitting at a hyphen in narrow columns. Longer code still wraps.
|
|
14
|
+
|
|
15
|
+
- [#39](https://github.com/nublson/notionic/pull/39) [`2d6256c`](https://github.com/nublson/notionic/commit/2d6256c46055aa99c61e7e786a7c3dd03542cad1) Thanks [@nublson](https://github.com/nublson)! - On touch screens, where heading anchors are always shown, the `#` is smaller and regular weight, so it reads apart from the heading instead of as part of it.
|
|
16
|
+
|
|
17
|
+
## 0.8.1
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- [#38](https://github.com/nublson/notionic/pull/38) [`32e56af`](https://github.com/nublson/notionic/commit/32e56afcbfec9ae5c7c1906cfc0d5c80797d8354) Thanks [@nublson](https://github.com/nublson)! - File, PDF, bookmark, embed and link preview cards press in slightly (scale 0.99, 150ms) when clicked; reduced motion keeps only the hover fill. Page link titles fade their underline on hover (150ms) like other links.
|
|
22
|
+
|
|
23
|
+
- [#38](https://github.com/nublson/notionic/pull/38) [`32e56af`](https://github.com/nublson/notionic/commit/32e56afcbfec9ae5c7c1906cfc0d5c80797d8354) Thanks [@nublson](https://github.com/nublson)! - Heading anchors fade in on hover (150ms) instead of popping in, and toggleable headings grow open and shrink closed with a fade (200ms) in browsers that support `::details-content` and `interpolate-size`; others open instantly as before. Reduced motion keeps only the fade.
|
|
24
|
+
|
|
25
|
+
- [#38](https://github.com/nublson/notionic/pull/38) [`32e56af`](https://github.com/nublson/notionic/commit/32e56afcbfec9ae5c7c1906cfc0d5c80797d8354) Thanks [@nublson](https://github.com/nublson)! - Links fade their underline to the text color on hover (150ms) instead of switching instantly.
|
|
26
|
+
|
|
27
|
+
- [#38](https://github.com/nublson/notionic/pull/38) [`32e56af`](https://github.com/nublson/notionic/commit/32e56afcbfec9ae5c7c1906cfc0d5c80797d8354) Thanks [@nublson](https://github.com/nublson)! - Switching tabs fades the new panel in (150ms) instead of swapping it instantly; the first panel still shows at once on page load. The registry's `notion-tabs` does the same.
|
|
28
|
+
|
|
29
|
+
- [#38](https://github.com/nublson/notionic/pull/38) [`32e56af`](https://github.com/nublson/notionic/commit/32e56afcbfec9ae5c7c1906cfc0d5c80797d8354) Thanks [@nublson](https://github.com/nublson)! - Table of contents entries fade their text and underline in on hover (150ms) instead of switching instantly.
|
|
30
|
+
|
|
31
|
+
- [#38](https://github.com/nublson/notionic/pull/38) [`32e56af`](https://github.com/nublson/notionic/commit/32e56afcbfec9ae5c7c1906cfc0d5c80797d8354) Thanks [@nublson](https://github.com/nublson)! - Toggles grow open and shrink closed with a fade (200ms), like toggleable headings, in browsers that support `::details-content` and `interpolate-size`. The registry's `notion-toggle` animates its Collapsible panel the same way, and a closed panel no longer leaves an 8px gap.
|
|
32
|
+
|
|
33
|
+
## 0.8.0
|
|
34
|
+
|
|
35
|
+
### Minor Changes
|
|
36
|
+
|
|
37
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Tabs render as real tabs instead of an accordion: a row of pills that scrolls sideways when the tabs don't fit, and the selected tab's content below. Still no JavaScript: each tab is a radio button, so the arrow keys switch tabs and scroll the selected one into view. Up to 20 tabs per block.
|
|
38
|
+
|
|
39
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Code blocks name their language as people write it ("TypeScript", "Plain text", "CSS") instead of Notion's lowercase API value, in the label and the accessible name. `languageName` is exported for custom code components; the registry's `notion-code` uses it.
|
|
40
|
+
|
|
41
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Child pages and links to pages render as rows with the page's icon (emoji, custom emoji or image, or a page icon without one) and a hover surface, and rows that follow each other sit close together, like subpages in Notion. `fetchBlocks` now fetches each subpage to attach its icon as `linkedPage`. Image icons on linked pages, dropped before, now render.
|
|
42
|
+
|
|
43
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Embeds and link previews get preview cards like bookmarks: `fetchBlocks` now fetches the page behind each one and attaches its title, description and image as `bookmarkPreview`. Without a preview, they render as a one-line card with a link icon instead of a bare URL that wraps. `bookmarkPreviews: false` turns all of these requests off.
|
|
44
|
+
|
|
45
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Videos: external links to video files (.mp4, .webm, .mov, .m4v, .ogv) play in a video player, as in Notion, instead of rendering as links. Players and YouTube embeds share the image outline and get a muted fill, so they keep their shape while loading.
|
|
46
|
+
|
|
47
|
+
### Patch Changes
|
|
48
|
+
|
|
49
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Native audio and video players turn dark with the page under the `dark` class, `data-theme="dark"` or `data-mode="dark"`, and are named by their caption for screen readers.
|
|
50
|
+
|
|
51
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Bookmarks without a preview render as a one-line card with a link icon instead of a bare URL that wraps. Preview cards show the URL without its protocol, let the title wrap to two lines on narrow cards, and give the image a muted surface while it loads.
|
|
52
|
+
|
|
53
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Refined callouts: icons sit in a fixed slot centered on the first line, so text lines up across callouts; callouts with a text color keep the default surface, and those with a background color drop the border; custom emoji icons now render.
|
|
54
|
+
|
|
55
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Columns go side by side based on the space they're in (36rem), not the screen width, so a renderer in a sidebar or a card stacks them instead of squeezing them.
|
|
56
|
+
|
|
57
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Refined dividers: twice the usual space on each side, so they read as section breaks; a heading right after a divider drops its own top margin.
|
|
58
|
+
|
|
59
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Embeds render as a one-line card with the URL and the caption below, instead of a bare link labelled by the caption or the raw URL.
|
|
60
|
+
|
|
61
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - The plain equation block shares the code block's surface and type, and tells screen readers it's an equation's LaTeX source.
|
|
62
|
+
|
|
63
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Files render as cards: an icon, the file name on one line (the full name as a tooltip when it truncates), its type, and the caption below, which was dropped before when a file had a name.
|
|
64
|
+
|
|
65
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Refined headings: the anchor link follows the last word when a heading wraps, with a larger hit area; headings balance their lines and use a tighter line height; every level keeps twice as much space above as below; colored headings span the full width; toggleable headings use a chevron that rotates when open, with their content aligned to the heading text.
|
|
66
|
+
|
|
67
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Images get an inset 1px outline, black at 10% (white at 10% in dark mode), so light images keep their edge against the page. It's the `--notionic-image-outline` variable, to change or turn off.
|
|
68
|
+
|
|
69
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Refined lists: nested bullets go disc, circle, square and nested numbers 1., a., i., as in Notion; bullets use the muted color; to-do checkboxes are drawn like shadcn's Checkbox instead of a dimmed disabled control, centered on the first line; nested to-dos are indented under their parent's text. The theme now also uses `primary-foreground` (see "Use your own theme tokens").
|
|
70
|
+
|
|
71
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Refined text rendering: long words, URLs and inline code wrap instead of overflowing narrow screens; body line height is 1.6; colored backgrounds bleed past the text so it stays aligned with the blocks around it; inline code and highlights keep their padding and corners across line breaks; underlines use the font's thickness; indents and borders use logical properties, so they mirror in right-to-left languages.
|
|
72
|
+
|
|
73
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - PDFs render as file cards, named after the file and typed "PDF", with the caption below, instead of a bare link showing the caption or the raw URL. Uploaded files keep their name when `transformFileUrl` returns a URL without one.
|
|
74
|
+
|
|
75
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Refined quotes: emphasis inside a quote turns roman so it still stands out against the italic, and quotes with a background color get padding on their open sides and rounded end corners.
|
|
76
|
+
|
|
77
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Refined tables: columns size to their content, up to 16rem before text wraps, and a table wider than the page scrolls sideways instead of squeezing every column to its narrowest; cells align to the start edge and numbers use tabular figures.
|
|
78
|
+
|
|
79
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Table of contents entries are 28px tap targets with no gaps between them.
|
|
80
|
+
|
|
81
|
+
- [#37](https://github.com/nublson/notionic/pull/37) [`c2161d2`](https://github.com/nublson/notionic/commit/c2161d22c411df042ea3ec3d3224c602a2e8d1c6) Thanks [@nublson](https://github.com/nublson)! - Refined toggles: the same chevron as toggleable headings replaces the browser's triangle, and a wrapped summary and the toggle's content line up on one edge.
|
|
82
|
+
|
|
83
|
+
## 0.7.0
|
|
84
|
+
|
|
85
|
+
### Minor Changes
|
|
86
|
+
|
|
87
|
+
- [#32](https://github.com/nublson/notionic/pull/32) [`df7d4ec`](https://github.com/nublson/notionic/commit/df7d4ecbab5eec6ee7a32eef50adb2881cfa9ea7) Thanks [@nublson](https://github.com/nublson)! - Interactive demos: pass your own React components as `demos={{ "morph-button": MorphButton }}`, and an embed of a URL ending in `/demos/<name>` renders that component in its place, in a frame with the embed's caption below. The URL's query reaches the demo as `params`. Replace the frame with `components.demo`.
|
|
88
|
+
|
|
89
|
+
## 0.6.0
|
|
90
|
+
|
|
91
|
+
### Minor Changes
|
|
92
|
+
|
|
93
|
+
- [#31](https://github.com/nublson/notionic/pull/31) [`74ce1db`](https://github.com/nublson/notionic/commit/74ce1db5d931342e0b4f0525d51337d6049f1c80) Thanks [@nublson](https://github.com/nublson)! - Bookmarks render as cards with the bookmarked page's title, description, image and favicon, like in Notion. `fetchBlocks` reads them from the page's Open Graph tags and attaches them as `bookmarkPreview`; pass `bookmarkPreviews: false` to make no requests outside Notion. Bookmarks without a preview still render as links.
|
|
94
|
+
|
|
95
|
+
## 0.5.0
|
|
96
|
+
|
|
97
|
+
### Minor Changes
|
|
98
|
+
|
|
99
|
+
- [#28](https://github.com/nublson/notionic/pull/28) [`0dfca00`](https://github.com/nublson/notionic/commit/0dfca00345d8233b850794246b144a54d460e42c) Thanks [@nublson](https://github.com/nublson)! - Follow any dark mode setup and renamed theme tokens
|
|
100
|
+
|
|
101
|
+
- [#27](https://github.com/nublson/notionic/pull/27) [`ba01dfd`](https://github.com/nublson/notionic/commit/ba01dfdf42934715ffa8a2e9d0597058e5047443) Thanks [@nublson](https://github.com/nublson)! - Notion's background colors (`--notionic-*-bg`) are now mixed from their text color and your theme's `--background`, so overriding the nine text colors (for example `--notionic-red: var(--destructive)`) themes the backgrounds too, in light and dark mode. `--notionic-bg-mix` sets their strength.
|
|
102
|
+
|
|
103
|
+
## 0.4.0
|
|
104
|
+
|
|
105
|
+
### Minor Changes
|
|
106
|
+
|
|
107
|
+
- [#22](https://github.com/nublson/notionic/pull/22) [`ed4a20a`](https://github.com/nublson/notionic/commit/ed4a20a0ecddb51efb507a618cb133c0cb452682) Thanks [@nublson](https://github.com/nublson)! - Add `notionic/markdown` with `blocksToMarkdown(blocks, options)`, which turns Notion blocks into GitHub-flavored Markdown for feeds and `llms.txt`. It supports every block `NotionRenderer` does, and takes the same `pageHref` and `transformFileUrl` options.
|
|
108
|
+
|
|
109
|
+
## 0.3.0
|
|
110
|
+
|
|
111
|
+
### Minor Changes
|
|
112
|
+
|
|
113
|
+
- [#18](https://github.com/nublson/notionic/pull/18) [`106aa72`](https://github.com/nublson/notionic/commit/106aa72d6903a8e6a622b8e5a67c4cdbd7a89010) Thanks [@nublson](https://github.com/nublson)! - **Breaking:** notionic now requires React 19. The peer dependency changes from `^18.0.0 || ^19.0.0` to `^19.0.0`. React 18 was never tested, and the package only targets React Server Components frameworks, which run React 19. If you're on React 18, stay on notionic 0.2.
|
|
114
|
+
|
|
115
|
+
- [#17](https://github.com/nublson/notionic/pull/17) [`8e279be`](https://github.com/nublson/notionic/commit/8e279bedc56421ad3fd280a99dec8795f0bab0e9) Thanks [@nublson](https://github.com/nublson)! - Add `transformFileUrl` to `NotionRenderer`, which rewrites the URLs of files uploaded to Notion (images, videos, files, PDFs, audio and callout icons), so they can point at a route that re-signs them before Notion's one-hour expiry. `resolveFileUrl` applies it in custom components.
|
|
116
|
+
|
|
117
|
+
### Patch Changes
|
|
118
|
+
|
|
119
|
+
- [#16](https://github.com/nublson/notionic/pull/16) [`63119a9`](https://github.com/nublson/notionic/commit/63119a996698fa551a9827d24a72447201b99ea3) Thanks [@nublson](https://github.com/nublson)! - Lint and format with Biome
|
|
120
|
+
|
|
121
|
+
## 0.2.0
|
|
122
|
+
|
|
123
|
+
### Minor Changes
|
|
124
|
+
|
|
125
|
+
- [#12](https://github.com/nublson/notionic/pull/12) [`05c92ea`](https://github.com/nublson/notionic/commit/05c92ea7365d60784063c01d1d07b9ade8e32eca) Thanks [@nublson](https://github.com/nublson)! - Add `fetchPage`, which returns a page's title, icon, cover, last edited time and properties with its blocks. `fetchBlocks` now attaches the linked page's title and icon to `link_to_page` blocks, which render as links through `pageHref`.
|
|
126
|
+
|
|
127
|
+
## 0.1.0
|
|
128
|
+
|
|
129
|
+
### Minor Changes
|
|
130
|
+
|
|
131
|
+
- [#10](https://github.com/nublson/notionic/pull/10) [`3ed98f2`](https://github.com/nublson/notionic/commit/3ed98f27cde1d25544c1b8f31a0dbd6cc523c9ff) Thanks [@nublson](https://github.com/nublson)! - Render table of contents, tabs, line breaks within text and date mentions, found by rendering a real Notion page. `collectHeadings` returns the page's headings with their anchor ids, for outlines. Equations inside text can be replaced with the new `inline_equation` component, which the registry's `notion-equation` (KaTeX) uses. The registry also adds `notion-tabs` (shadcn Tabs) and `notion-mermaid` (diagrams).
|
|
132
|
+
|
|
133
|
+
## 0.0.1
|
|
134
|
+
|
|
135
|
+
- First release: `NotionRenderer` for Notion API blocks as React Server Components, styled with Tailwind CSS and shadcn/ui tokens; `fetchBlocks` in `notionic/fetch`; and `notionic/styles.css`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nubelson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,262 @@
|
|
|
1
|
-
#
|
|
1
|
+
# notionic
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/notionic)
|
|
4
|
+
[](https://github.com/nublson/notionic/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/nublson/notionic/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
Render blocks from the [official Notion API](https://developers.notion.com) as React Server Components, styled with Tailwind CSS and shadcn/ui's theme tokens.
|
|
8
|
+
|
|
9
|
+
- **Server-first.** Every block renders on the server; notionic ships no client JavaScript.
|
|
10
|
+
- **Follows your theme.** Components use shadcn/ui tokens (`foreground`, `muted`, `border`…), so they match your app, dark mode included.
|
|
11
|
+
- **Replaceable.** Swap the component for any block type.
|
|
12
|
+
- **Zero runtime dependencies** besides React.
|
|
13
|
+
|
|
14
|
+
> Early development. The API may change before 1.0.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add notionic @notionhq/client
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
notionic needs React 19. `@notionhq/client` is only needed to fetch blocks with `notionic/fetch`.
|
|
23
|
+
|
|
24
|
+
## Set up Tailwind CSS
|
|
25
|
+
|
|
26
|
+
notionic needs Tailwind CSS v4. Import its styles after Tailwind:
|
|
27
|
+
|
|
28
|
+
```css
|
|
29
|
+
@import "tailwindcss";
|
|
30
|
+
@import "notionic/styles.css";
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This one line tells Tailwind to scan notionic's components, and defines Notion's nine colors as `--notionic-*` variables. Their dark values apply under the `dark` class, `data-theme="dark"` or `data-mode="dark"`, and on pages whose [`color-scheme`](https://developer.mozilla.org/docs/Web/CSS/color-scheme) is dark, so a page that follows `prefers-color-scheme` gets them by declaring `color-scheme: light dark`.
|
|
34
|
+
|
|
35
|
+
### Match Notion's colors to your brand
|
|
36
|
+
|
|
37
|
+
Override the text colors after the import. Each background color (`--notionic-red-bg`…) is mixed from its text color and your `--background`, so it follows on its own:
|
|
38
|
+
|
|
39
|
+
```css
|
|
40
|
+
:root,
|
|
41
|
+
.dark {
|
|
42
|
+
--notionic-red: var(--destructive);
|
|
43
|
+
--notionic-blue: var(--primary);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Mapping to tokens that already switch in dark mode, like shadcn's, keeps both modes right. To make every background stronger or subtler, set `--notionic-bg-mix` (default `14%`, `22%` in dark mode); to set one background directly, override its `--notionic-*-bg`.
|
|
48
|
+
|
|
49
|
+
### Use your own theme tokens
|
|
50
|
+
|
|
51
|
+
The components use shadcn/ui's color tokens, and Tailwind only generates their classes if your theme defines them. If your design system names them differently, map yours to these eight, after importing notionic:
|
|
52
|
+
|
|
53
|
+
```css
|
|
54
|
+
@theme inline {
|
|
55
|
+
--color-background: var(--color-page);
|
|
56
|
+
--color-foreground: var(--color-text);
|
|
57
|
+
--color-muted: var(--color-surface);
|
|
58
|
+
--color-muted-foreground: var(--color-text-subtle);
|
|
59
|
+
--color-border: var(--color-line);
|
|
60
|
+
--color-ring: var(--color-focus);
|
|
61
|
+
--color-primary: var(--color-brand);
|
|
62
|
+
--color-primary-foreground: var(--color-on-brand);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Replace each right-hand side with your own token. notionic needs no others.
|
|
67
|
+
|
|
68
|
+
## Render a page
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { Client } from "@notionhq/client";
|
|
72
|
+
import { NotionRenderer } from "notionic";
|
|
73
|
+
import { fetchBlocks } from "notionic/fetch";
|
|
74
|
+
|
|
75
|
+
const notion = new Client({ auth: process.env.NOTION_TOKEN });
|
|
76
|
+
|
|
77
|
+
export default async function Page() {
|
|
78
|
+
const blocks = await fetchBlocks(notion, "your-page-id");
|
|
79
|
+
return <NotionRenderer blocks={blocks} />;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
To also get the page's title, icon, cover and properties, use `fetchPage(notion, pageId)`, which returns `{ page, blocks }`.
|
|
84
|
+
|
|
85
|
+
`fetchBlocks` fetches every block of the page with its nested blocks, following pagination. It limits requests in flight (`concurrency`, default 3) and nesting depth (`maxDepth`, default 5). It doesn't cache: use your framework's cache, such as Next.js's `"use cache"`.
|
|
86
|
+
|
|
87
|
+
For bookmarks, embeds and link previews, `fetchBlocks` also fetches the linked page and reads its title, description, image and favicon, so the block renders as a card like a Notion bookmark. A page that's slow (over 5 seconds), unreachable or untitled renders as a one-line card with the URL, and private network addresses are never fetched. Pass `bookmarkPreviews: false` to make no requests outside Notion.
|
|
88
|
+
|
|
89
|
+
You can also pass blocks you fetched yourself: `NotionRenderer` takes the API's block objects with nested blocks attached as `children` (the `NotionBlock` type).
|
|
90
|
+
|
|
91
|
+
## Options
|
|
92
|
+
|
|
93
|
+
### Link Notion pages to your routes
|
|
94
|
+
|
|
95
|
+
Links to other Notion pages point to notion.so by default. Map them to your own routes with `pageHref`, or return `null` to render the text without a link:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
<NotionRenderer
|
|
99
|
+
blocks={blocks}
|
|
100
|
+
pageHref={(pageId) => routes.get(pageId) ?? null}
|
|
101
|
+
/>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`pageId` is 32 lowercase hex characters, without dashes.
|
|
105
|
+
|
|
106
|
+
### Replace a block's component
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import { NotionRenderer, RichText, type BlockProps } from "notionic";
|
|
110
|
+
|
|
111
|
+
function Callout({ block, context, children }: BlockProps<"callout">) {
|
|
112
|
+
return (
|
|
113
|
+
<aside className="rounded-lg border p-4">
|
|
114
|
+
<RichText text={block.callout.rich_text} context={context} />
|
|
115
|
+
{children}
|
|
116
|
+
</aside>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
<NotionRenderer blocks={blocks} components={{ callout: Callout }} />;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`children` holds the block's nested blocks, already rendered.
|
|
124
|
+
|
|
125
|
+
`notion-mermaid` loads Mermaid in the browser only on pages with a diagram, follows the `dark` class, and keeps showing the code until the diagram draws, or if its source doesn't parse. Wrap the code component you use: `withMermaid(NotionCode)`, or `withMermaid(defaultComponents.code)`.
|
|
126
|
+
|
|
127
|
+
Equations inside text have their own key, `inline_equation`, whose component gets the equation's LaTeX as `expression`.
|
|
128
|
+
|
|
129
|
+
### Interactive demos
|
|
130
|
+
|
|
131
|
+
Put your own React components in a page, like the demos in a design engineering blog. Each demo is a component in your project, so it can use anything you install (Motion, Sonner, charts, 3D). In Notion, an embed of a URL ending in `/demos/<name>` marks where it goes, and its caption goes below it.
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
// components/demos/morph-button.tsx
|
|
135
|
+
"use client";
|
|
136
|
+
export function MorphButton({ params }: DemoProps) { /* … */ }
|
|
137
|
+
|
|
138
|
+
// app/page.tsx
|
|
139
|
+
<NotionRenderer blocks={blocks} demos={{ "morph-button": MorphButton }} />;
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
In Notion, add `/embed` with `https://your-site.com/demos/morph-button`. Only the path counts, so the same embed renders on localhost and previews. The URL's query reaches the demo as `params`: `?speed=slow` is `{ speed: "slow" }`. Unknown names render as links.
|
|
143
|
+
|
|
144
|
+
To see the demo inside Notion while you write, add a page at `/demos/[name]` that renders the demo alone:
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
// app/demos/[name]/page.tsx
|
|
148
|
+
export default async function DemoPage({ params }: { params: Promise<{ name: string }> }) {
|
|
149
|
+
const { name } = await params;
|
|
150
|
+
const Demo = Object.hasOwn(demos, name) ? demos[name] : undefined;
|
|
151
|
+
if (!Demo) notFound();
|
|
152
|
+
return <main className="grid min-h-svh place-items-center"><Demo name={name} params={{}} /></main>;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Demos render in `DemoFrame`, a bordered stage with the caption centered below. Replace it with `components={{ demo: MyFrame }}`; it gets the demo as `children`, plus `name`, `caption` and `context`. Only the demos a page uses send JavaScript to the browser.
|
|
157
|
+
|
|
158
|
+
## Supported blocks
|
|
159
|
+
|
|
160
|
+
Paragraph (with line breaks), headings 1–4 (including toggleable headings), bulleted, numbered and to-do lists, quote, callout, toggle, divider, equation, code, table, columns, tabs, table of contents, synced block, image, video (YouTube or file), audio, file, PDF, bookmark (a card with the page's title, description and image, from `fetchBlocks`), embed and link preview (cards like bookmarks, or for an embed one of your demos), and child page and link to page (with their icons and titles, from `fetchBlocks`). Date mentions render as `<time>`.
|
|
161
|
+
|
|
162
|
+
Notion's heading 1 renders as `<h2>`, since your page title is the `<h1>`; heading 2 as `<h3>`, and so on. Headings get ids and anchor links.
|
|
163
|
+
|
|
164
|
+
Tabs render as real tabs with no JavaScript: a row of pills that scrolls sideways, and one panel at a time. To use shadcn's Tabs component instead, add `notion-tabs` from the shadcn registry.
|
|
165
|
+
|
|
166
|
+
Code renders as plain text. For syntax highlighting, use `notion-code` from the shadcn registry below. Equations, in blocks and inside text, show their LaTeX source; `notion-equation` typesets them with KaTeX.
|
|
167
|
+
|
|
168
|
+
Unsupported blocks render nothing, with a warning in development.
|
|
169
|
+
|
|
170
|
+
### Page outline
|
|
171
|
+
|
|
172
|
+
`collectHeadings(blocks)` returns the page's headings in reading order, with the same anchor ids the rendered headings get, for a sidebar outline:
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
import { collectHeadings } from "notionic";
|
|
176
|
+
|
|
177
|
+
const outline = collectHeadings(blocks); // [{ id: "intro", text: "Intro", level: 1, blockId }]
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## shadcn/ui components
|
|
181
|
+
|
|
182
|
+
notionic's shadcn registry has versions of some blocks built on your own shadcn/ui components. The shadcn CLI copies them into your project, with the shadcn components they use, so you can edit them.
|
|
183
|
+
|
|
184
|
+
| Item | Built on |
|
|
185
|
+
| --- | --- |
|
|
186
|
+
| `notion-code` | Code Block, highlighted on the server with Shiki, with a copy button |
|
|
187
|
+
| `notion-callout` | Alert |
|
|
188
|
+
| `notion-table` | Table |
|
|
189
|
+
| `notion-toggle` | Collapsible |
|
|
190
|
+
| `notion-tabs` | Tabs, with every panel kept in the HTML |
|
|
191
|
+
| `notion-equation` | KaTeX, rendered on the server: equation blocks and equations inside text |
|
|
192
|
+
| `notion-mermaid` | Mermaid: draws `mermaid` code blocks as diagrams, wrapping your code component |
|
|
193
|
+
|
|
194
|
+
Add the registry to your `components.json`:
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"registries": {
|
|
199
|
+
"@notionic": "https://notionic.dev/r/{name}.json"
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Then add the items you want, and pass them to `components`:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
npx shadcn@latest add @notionic/notion-code @notionic/notion-callout @notionic/notion-table @notionic/notion-toggle @notionic/notion-tabs @notionic/notion-equation @notionic/notion-mermaid
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
import { NotionCallout } from "@/components/notion-callout";
|
|
212
|
+
import { NotionCode } from "@/components/notion-code";
|
|
213
|
+
import { NotionTable } from "@/components/notion-table";
|
|
214
|
+
import { NotionToggle } from "@/components/notion-toggle";
|
|
215
|
+
import { NotionTabs } from "@/components/notion-tabs";
|
|
216
|
+
import { NotionEquation, NotionInlineEquation } from "@/components/notion-equation";
|
|
217
|
+
import { withMermaid } from "@/components/notion-mermaid";
|
|
218
|
+
|
|
219
|
+
<NotionRenderer
|
|
220
|
+
blocks={blocks}
|
|
221
|
+
components={{
|
|
222
|
+
code: withMermaid(NotionCode),
|
|
223
|
+
callout: NotionCallout,
|
|
224
|
+
table: NotionTable,
|
|
225
|
+
toggle: NotionToggle,
|
|
226
|
+
tab: NotionTabs,
|
|
227
|
+
equation: NotionEquation,
|
|
228
|
+
inline_equation: NotionInlineEquation,
|
|
229
|
+
}}
|
|
230
|
+
/>;
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`notion-code` highlights the languages listed in `lib/highlighter.tsx`, which comes with shadcn's Code Block. Add a language there to highlight it; others render as plain text.
|
|
234
|
+
|
|
235
|
+
## Markdown
|
|
236
|
+
|
|
237
|
+
`notionic/markdown` turns the same blocks into GitHub-flavored Markdown, for RSS feeds, `llms.txt` and other plain-text uses:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { blocksToMarkdown } from "notionic/markdown";
|
|
241
|
+
|
|
242
|
+
const markdown = `# ${page.title}\n\n${blocksToMarkdown(blocks, { pageHref, transformFileUrl })}`;
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
It takes the same `pageHref` and `transformFileUrl` as `NotionRenderer`, so links and files match the HTML. Notion's heading 1 becomes `##`, so add the page title yourself. Toggles become `<details>`, equations `$…$` and `$$…$$`.
|
|
246
|
+
|
|
247
|
+
## Uploaded files
|
|
248
|
+
|
|
249
|
+
Notion's URLs for uploaded files (images, videos, files, icons) expire after an hour, which is shorter than most pages are cached. Pass `transformFileUrl` to point them at a route of yours that fetches a fresh URL:
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
<NotionRenderer
|
|
253
|
+
blocks={blocks}
|
|
254
|
+
transformFileUrl={({ blockId }) => `/api/notion/file/${blockId}`}
|
|
255
|
+
/>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The route retrieves the block with the Notion API (`notion.blocks.retrieve`) and redirects to its file's current URL. notionic.dev's route is a working example: `apps/web/app/api/notion/file/[blockId]/route.ts`. External URLs never go through `transformFileUrl`.
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
MIT
|
package/dist/fetch.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { C as IdRequest, S as ListBlockChildrenResponse, T as PartialPageObjectResponse, f as NotionPage, u as NotionBlock, w as PageObjectResponse, x as ListBlockChildrenParameters } from "./types-DOxr5KpT.js";
|
|
2
|
+
//#region ../../node_modules/.pnpm/@notionhq+client@5.27.1/node_modules/@notionhq/client/build/src/api-endpoints/pages.d.ts
|
|
3
|
+
type GetPagePathParameters = {
|
|
4
|
+
page_id: IdRequest;
|
|
5
|
+
};
|
|
6
|
+
type GetPageQueryParameters = {
|
|
7
|
+
filter_properties?: Array<string>;
|
|
8
|
+
};
|
|
9
|
+
type GetPageParameters = GetPagePathParameters & GetPageQueryParameters;
|
|
10
|
+
type GetPageResponse = PartialPageObjectResponse | PageObjectResponse;
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/fetch.d.ts
|
|
13
|
+
/**
|
|
14
|
+
* The part of the Notion client notionic uses. A `Client` from
|
|
15
|
+
* `@notionhq/client` satisfies it. Without `pages`, `link_to_page` blocks
|
|
16
|
+
* get no titles and subpages no icons.
|
|
17
|
+
*/
|
|
18
|
+
export type NotionClientLike = {
|
|
19
|
+
blocks: {
|
|
20
|
+
children: {
|
|
21
|
+
list(args: ListBlockChildrenParameters): Promise<ListBlockChildrenResponse>;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
pages?: {
|
|
25
|
+
retrieve(args: GetPageParameters): Promise<GetPageResponse>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export type FetchBlocksOptions = {
|
|
29
|
+
/** How many levels of nested blocks to fetch below the top level. Default 5. */
|
|
30
|
+
maxDepth?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Max child-block requests in flight per level. Default 3, Notion's average
|
|
33
|
+
* rate limit per second.
|
|
34
|
+
*/
|
|
35
|
+
concurrency?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Fetch the page behind each bookmark, embed and link preview to read its
|
|
38
|
+
* title, description and image for a preview card. Default true. A page
|
|
39
|
+
* that's slow, unreachable or has no title renders as a one-line card.
|
|
40
|
+
*/
|
|
41
|
+
bookmarkPreviews?: boolean;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Every block of a page (or any block), with nested blocks attached as
|
|
45
|
+
* `children`, ready for `<NotionRenderer>`.
|
|
46
|
+
*
|
|
47
|
+
* Child pages and databases are separate documents, so their content is not
|
|
48
|
+
* fetched. Caching is left to your framework.
|
|
49
|
+
*/
|
|
50
|
+
export declare function fetchBlocks(client: NotionClientLike, blockId: string, options?: FetchBlocksOptions): Promise<NotionBlock[]>;
|
|
51
|
+
/**
|
|
52
|
+
* A page's metadata (title, icon, cover, properties…) and every block of it.
|
|
53
|
+
* Throws if the page doesn't exist or isn't shared with the integration.
|
|
54
|
+
*/
|
|
55
|
+
export declare function fetchPage(client: NotionClientLike & {
|
|
56
|
+
pages: NonNullable<NotionClientLike["pages"]>;
|
|
57
|
+
}, pageId: string, options?: FetchBlocksOptions): Promise<{
|
|
58
|
+
page: NotionPage;
|
|
59
|
+
blocks: NotionBlock[];
|
|
60
|
+
}>;
|
|
61
|
+
//#endregion
|
|
62
|
+
export {};
|