notro-loader 0.0.1 → 0.0.2

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 mosugi
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.
1
+ MIT License
2
+
3
+ Copyright (c) 2024 mosugi
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,276 +1,276 @@
1
- # notro-loader
2
-
3
- ![npm](https://img.shields.io/npm/v/notro-loader)
4
- ![License: MIT](https://img.shields.io/badge/license-MIT-blue)
5
-
6
- An Astro Content Loader library that fetches Notion database content via the [Markdown Content API](https://developers.notion.com/) into [Astro Content Collections](https://docs.astro.build/en/guides/content-collections/).
7
-
8
- > [!TIP]
9
- > Sample project: [notro](https://github.com/mosugi/notro)
10
-
11
- ## What NotroContent renders
12
-
13
- `NotroContent` compiles Notion markdown into HTML. Each Notion block type maps to a semantic HTML element by default. You can replace any element with your own styled component via the `components` prop.
14
-
15
- | Notion block | Default HTML | notro-ui component |
16
- |---|---|---|
17
- | Paragraph | `<p>` | `ColoredParagraph` |
18
- | Heading 1–4 | `<h1>`–`<h4>` | `H1`–`H4` |
19
- | Callout | `<aside>` | `Callout` |
20
- | Quote | `<blockquote>` | `Quote` |
21
- | Toggle | `<details>` + `<summary>` | `Toggle` + `ToggleTitle` |
22
- | Divider | `<hr>` | — |
23
- | Code | `<pre>` | — |
24
- | Image | `<img>` | `ImageBlock` |
25
- | Video | `<figure>` | `Video` |
26
- | Audio | `<figure>` | `Audio` |
27
- | File | `<div>` | `FileBlock` |
28
- | PDF | `<figure>` | `PdfBlock` |
29
- | Table | `<table>` | `TableBlock` |
30
- | Table of contents | `<nav>` | `TableOfContents` |
31
- | Columns / Column | `<div>` / `<div>` | `Columns` / `Column` |
32
- | Page link | `<a>` | `PageRef` |
33
- | Database link | `<a>` | `DatabaseRef` |
34
- | Empty block | `<div>` | `EmptyBlock` |
35
- | Inline text (colored/underline) | `<span>` | `StyledSpan` |
36
- | @mention | `<span>` | `Mention` |
37
- | Date mention | `<time>` | `MentionDate` |
38
-
39
- `notro-ui` is an optional style layer. See [notro-ui](https://github.com/mosugi/notro/tree/main/packages/notro-ui) for details.
40
-
41
- ## Installation
42
-
43
- ```sh
44
- npx astro add notro-loader
45
- ```
46
-
47
- This installs the package and automatically adds the `notro()` integration to `astro.config.mjs`.
48
-
49
- Alternatively, install manually:
50
-
51
- ```sh
52
- npm install notro-loader
53
- ```
54
-
55
- ## Setup
56
-
57
- ### 1. `astro.config.mjs`
58
-
59
- `astro add notro-loader` configures this automatically. If you installed manually, add:
60
-
61
- ```js
62
- import { defineConfig } from "astro/config";
63
- import { notro } from "notro-loader/integration";
64
-
65
- export default defineConfig({
66
- integrations: [notro()],
67
- });
68
- ```
69
-
70
- This registers `@astrojs/mdx` with the required plugin pipeline and the Astro JSX renderer that `NotroContent` depends on at runtime.
71
-
72
- ### 2. `src/content.config.ts`
73
-
74
- Define your collection using the `loader` function. Extend `pageWithMarkdownSchema` with your database properties. Use the `notroProperties` shorthand for concise property schemas.
75
-
76
- ```typescript
77
- import { defineCollection } from "astro:content";
78
- import { loader, pageWithMarkdownSchema, notroProperties } from "notro-loader";
79
- import { z } from "zod";
80
-
81
- const posts = defineCollection({
82
- loader: loader({
83
- queryParameters: {
84
- data_source_id: import.meta.env.NOTION_DATASOURCE_ID,
85
- filter: {
86
- property: "Public",
87
- checkbox: { equals: true },
88
- },
89
- },
90
- clientOptions: {
91
- auth: import.meta.env.NOTION_TOKEN,
92
- },
93
- }),
94
- schema: pageWithMarkdownSchema.extend({
95
- properties: z.object({
96
- Name: notroProperties.title,
97
- Description: notroProperties.richText,
98
- Public: notroProperties.checkbox,
99
- Tags: notroProperties.multiSelect,
100
- Date: notroProperties.date,
101
- }),
102
- }),
103
- });
104
-
105
- export const collections = { posts };
106
- ```
107
-
108
- ### 3. Page component
109
-
110
- #### Option A — Headless (no styling)
111
-
112
- `NotroContent` from `notro-loader` renders semantic HTML with no classes.
113
-
114
- ```astro
115
- ---
116
- import { NotroContent, getPlainText } from "notro-loader";
117
-
118
- const { entry } = Astro.props;
119
- const title = getPlainText(entry.data.properties.Name);
120
- ---
121
-
122
- <h1>{title}</h1>
123
- <NotroContent markdown={entry.data.markdown} />
124
- ```
125
-
126
- #### Option B — With notro-ui
127
-
128
- Install the styled components once:
129
-
130
- ```sh
131
- npx notro-ui init
132
- ```
133
-
134
- Then pass the component map to `NotroContent`:
135
-
136
- ```astro
137
- ---
138
- import { NotroContent, getPlainText } from "notro-loader";
139
- import { notroComponents } from "@/components/notro";
140
-
141
- const { entry } = Astro.props;
142
- ---
143
-
144
- <NotroContent markdown={entry.data.markdown} components={notroComponents} />
145
- ```
146
-
147
- Components are copied into `src/components/notro/` so you can edit them directly.
148
-
149
- ## Markdown processing (remark-nfm)
150
-
151
- `notro-loader` delegates Notion Markdown preprocessing and directive syntax support to the [`remark-nfm`](https://www.npmjs.com/package/remark-nfm) package.
152
-
153
- `remark-nfm` is used inside notro-loader's MDX compile pipeline and is applied automatically when using `NotroContent`.
154
-
155
- If you want to use `remark-nfm` directly (in a custom unified pipeline or `@mdx-js/mdx`'s `evaluate()`), import it from the `remark-nfm` package directly rather than from `notro-loader`.
156
-
157
- ```ts
158
- // ✅ Import directly from remark-nfm
159
- import { remarkNfm, preprocessNotionMarkdown } from "remark-nfm";
160
-
161
- // ❌ Not needed from notro-loader (internal use only)
162
- // import { remarkNfm } from "notro-loader";
163
- ```
164
-
165
- ## Notion API limitations
166
-
167
- > Reference: [Retrieve a page as Markdown – Notion API](https://developers.notion.com/reference/retrieve-page-markdown)
168
-
169
- ### Content truncation (`truncated`)
170
-
171
- `GET /v1/pages/{page_id}/markdown` truncates content at approximately **20,000 blocks**.
172
-
173
- - Detectable via `truncated: true` in the response, but **there is no pagination API to fetch the rest**
174
- - notro logs a warning when `truncated === true` and continues the build with the available content
175
- - Workaround: split large Notion pages into multiple smaller pages
176
-
177
- ```
178
- ⚠ Page abc123: markdown content was truncated by the Notion API (~20,000 block limit).
179
- No pagination is available for this endpoint.
180
- Consider splitting this Notion page into smaller pages to avoid truncation.
181
- ```
182
-
183
- ### Unrenderable blocks (`unknown_block_ids`)
184
-
185
- `unknown_block_ids` in the response lists block IDs that the Notion API could not convert to Markdown (unsupported block types, etc.).
186
-
187
- - These blocks are **silently omitted** from the `markdown` field
188
- - There is no way to retrieve their content via this endpoint
189
- - notro logs the block IDs as a warning and continues the build
190
-
191
- ```
192
- ⚠ Page abc123: 2 block(s) could not be rendered to Markdown by the Notion API and were omitted.
193
- Block IDs: xxxxxxxx-..., yyyyyyyy-...
194
- ```
195
-
196
- ### API errors and automatic retries
197
-
198
- | Error | Handling |
199
- |---|---|
200
- | `429 rate_limited` / `500 internal_server_error` / `503 service_unavailable` | Retry with exponential backoff (1s / 2s / 4s, up to 3 times) |
201
- | `401 unauthorized` / `403 restricted_resource` / `404 object_not_found` | No retry. Logs a warning and skips the page |
202
- | Other unexpected errors | Logs a warning and skips the page (build continues) |
203
-
204
- ## Environment variables
205
-
206
- | Variable | Description |
207
- |---|---|
208
- | `NOTION_TOKEN` | Notion Internal Integration Token |
209
- | `NOTION_DATASOURCE_ID` | Notion data source ID |
210
-
211
- ## API Reference
212
-
213
- ### `loader(options)`
214
-
215
- Astro Content Loader. Pass Notion API `dataSources.query` parameters via `queryParameters`.
216
-
217
- ### `pageWithMarkdownSchema`
218
-
219
- Base Zod schema returned by the loader. Extends `pageObjectResponseSchema` with `markdown: z.string()`. Extend with `.extend()` for custom schemas.
220
-
221
- ### Property schemas
222
-
223
- Use the `notroProperties` shorthand to define database property types in `content.config.ts` (see [`notroProperties`](#notroproperties)).
224
-
225
- Individual schemas (e.g. `titlePropertyPageObjectResponseSchema`) remain exported for backwards compatibility.
226
-
227
- ### Components
228
-
229
- | Component | Description |
230
- |---|---|
231
- | `NotroContent` | Renders Notion Markdown to HTML. Unstyled by default; pass `components` to customize |
232
- | `DatabaseCover` | Renders a Notion cover image with optimization |
233
- | `DatabaseProperty` | Renders a Notion property value by type |
234
- | `compileMdxCached` | Low-level MDX compile API. Use when building a custom `NotroContent` |
235
-
236
- ### `notroProperties`
237
-
238
- Zod schema shorthands for defining property schemas in `content.config.ts`. Each key maps to a Notion property type.
239
-
240
- ```typescript
241
- import { notroProperties } from "notro-loader";
242
-
243
- // notroProperties.title → titlePropertyPageObjectResponseSchema
244
- // notroProperties.richText → richTextPropertyPageObjectResponseSchema
245
- // notroProperties.checkbox → checkboxPropertyPageObjectResponseSchema
246
- // notroProperties.multiSelect → multiSelectPropertyPageObjectResponseSchema
247
- // notroProperties.select → selectPropertyPageObjectResponseSchema
248
- // notroProperties.date → datePropertyPageObjectResponseSchema
249
- // notroProperties.number → numberPropertyPageObjectResponseSchema
250
- // notroProperties.url → urlPropertyPageObjectResponseSchema
251
- // notroProperties.email → emailPropertyPageObjectResponseSchema
252
- // notroProperties.phoneNumber → phoneNumberPropertyPageObjectResponseSchema
253
- // notroProperties.files → filesPropertyPageObjectResponseSchema
254
- // notroProperties.people → peoplePropertyPageObjectResponseSchema
255
- // notroProperties.relation → relationPropertyPageObjectResponseSchema
256
- // notroProperties.rollup → rollupPropertyPageObjectResponseSchema
257
- // notroProperties.formula → formulaPropertyPageObjectResponseSchema
258
- // notroProperties.uniqueId → uniqueIdPropertyPageObjectResponseSchema
259
- // notroProperties.status → statusPropertyPageObjectResponseSchema
260
- // notroProperties.createdTime → createdTimePropertyPageObjectResponseSchema
261
- // notroProperties.createdBy → createdByPropertyPageObjectResponseSchema
262
- // notroProperties.lastEditedTime → lastEditedTimePropertyPageObjectResponseSchema
263
- // notroProperties.lastEditedBy → lastEditedByPropertyPageObjectResponseSchema
264
- // notroProperties.button → buttonPropertyPageObjectResponseSchema
265
- // notroProperties.verification → verificationPropertyPageObjectResponseSchema
266
- ```
267
-
268
- ### Utilities
269
-
270
- | Function | Description |
271
- |---|---|
272
- | `getPlainText(property)` | Extracts plain text from Title, Rich Text, Select, Multi-select, Number, URL, Email, Phone, Date, and Unique ID properties |
273
- | `getMultiSelect(property)` | Returns the options array for a multi-select property. Returns an empty array for unsupported types or `undefined` — no type guard needed |
274
- | `hasTag(property, tagName)` | Returns whether a multi-select property contains the given tag name. Safe to call without a type guard |
275
- | `buildLinkToPages(entries, options)` | Builds a `linkToPages` map from collection entries. Pass to `NotroContent` for resolving inter-page Notion links |
276
- | `colorToCSS(color)` | Converts a Notion color name to an inline CSS style string (for use in custom components) |
1
+ # notro-loader
2
+
3
+ ![npm](https://img.shields.io/npm/v/notro-loader)
4
+ ![License: MIT](https://img.shields.io/badge/license-MIT-blue)
5
+
6
+ An Astro Content Loader library that fetches Notion database content via the [Markdown Content API](https://developers.notion.com/) into [Astro Content Collections](https://docs.astro.build/en/guides/content-collections/).
7
+
8
+ > [!TIP]
9
+ > Sample project: [notro](https://github.com/mosugi/notro)
10
+
11
+ ## What NotroContent renders
12
+
13
+ `NotroContent` compiles Notion markdown into HTML. Each Notion block type maps to a semantic HTML element by default. You can replace any element with your own styled component via the `components` prop.
14
+
15
+ | Notion block | Default HTML | notro-ui component |
16
+ |---|---|---|
17
+ | Paragraph | `<p>` | `ColoredParagraph` |
18
+ | Heading 1–4 | `<h1>`–`<h4>` | `H1`–`H4` |
19
+ | Callout | `<aside>` | `Callout` |
20
+ | Quote | `<blockquote>` | `Quote` |
21
+ | Toggle | `<details>` + `<summary>` | `Toggle` + `ToggleTitle` |
22
+ | Divider | `<hr>` | — |
23
+ | Code | `<pre>` | — |
24
+ | Image | `<img>` | `ImageBlock` |
25
+ | Video | `<figure>` | `Video` |
26
+ | Audio | `<figure>` | `Audio` |
27
+ | File | `<div>` | `FileBlock` |
28
+ | PDF | `<figure>` | `PdfBlock` |
29
+ | Table | `<table>` | `TableBlock` |
30
+ | Table of contents | `<nav>` | `TableOfContents` |
31
+ | Columns / Column | `<div>` / `<div>` | `Columns` / `Column` |
32
+ | Page link | `<a>` | `PageRef` |
33
+ | Database link | `<a>` | `DatabaseRef` |
34
+ | Empty block | `<div>` | `EmptyBlock` |
35
+ | Inline text (colored/underline) | `<span>` | `StyledSpan` |
36
+ | @mention | `<span>` | `Mention` |
37
+ | Date mention | `<time>` | `MentionDate` |
38
+
39
+ `notro-ui` is an optional style layer. See [notro-ui](https://github.com/mosugi/notro/tree/main/packages/notro-ui) for details.
40
+
41
+ ## Installation
42
+
43
+ ```sh
44
+ npx astro add notro-loader
45
+ ```
46
+
47
+ This installs the package and automatically adds the `notro()` integration to `astro.config.mjs`.
48
+
49
+ Alternatively, install manually:
50
+
51
+ ```sh
52
+ npm install notro-loader
53
+ ```
54
+
55
+ ## Setup
56
+
57
+ ### 1. `astro.config.mjs`
58
+
59
+ `astro add notro-loader` configures this automatically. If you installed manually, add:
60
+
61
+ ```js
62
+ import { defineConfig } from "astro/config";
63
+ import { notro } from "notro-loader/integration";
64
+
65
+ export default defineConfig({
66
+ integrations: [notro()],
67
+ });
68
+ ```
69
+
70
+ This registers `@astrojs/mdx` with the required plugin pipeline and the Astro JSX renderer that `NotroContent` depends on at runtime.
71
+
72
+ ### 2. `src/content.config.ts`
73
+
74
+ Define your collection using the `loader` function. Extend `pageWithMarkdownSchema` with your database properties. Use the `notroProperties` shorthand for concise property schemas.
75
+
76
+ ```typescript
77
+ import { defineCollection } from "astro:content";
78
+ import { loader, pageWithMarkdownSchema, notroProperties } from "notro-loader";
79
+ import { z } from "zod";
80
+
81
+ const posts = defineCollection({
82
+ loader: loader({
83
+ queryParameters: {
84
+ data_source_id: import.meta.env.NOTION_DATASOURCE_ID,
85
+ filter: {
86
+ property: "Public",
87
+ checkbox: { equals: true },
88
+ },
89
+ },
90
+ clientOptions: {
91
+ auth: import.meta.env.NOTION_TOKEN,
92
+ },
93
+ }),
94
+ schema: pageWithMarkdownSchema.extend({
95
+ properties: z.object({
96
+ Name: notroProperties.title,
97
+ Description: notroProperties.richText,
98
+ Public: notroProperties.checkbox,
99
+ Tags: notroProperties.multiSelect,
100
+ Date: notroProperties.date,
101
+ }),
102
+ }),
103
+ });
104
+
105
+ export const collections = { posts };
106
+ ```
107
+
108
+ ### 3. Page component
109
+
110
+ #### Option A — Headless (no styling)
111
+
112
+ `NotroContent` from `notro-loader` renders semantic HTML with no classes.
113
+
114
+ ```astro
115
+ ---
116
+ import { NotroContent, getPlainText } from "notro-loader";
117
+
118
+ const { entry } = Astro.props;
119
+ const title = getPlainText(entry.data.properties.Name);
120
+ ---
121
+
122
+ <h1>{title}</h1>
123
+ <NotroContent markdown={entry.data.markdown} />
124
+ ```
125
+
126
+ #### Option B — With notro-ui
127
+
128
+ Install the styled components once:
129
+
130
+ ```sh
131
+ npx notro-ui init
132
+ ```
133
+
134
+ Then pass the component map to `NotroContent`:
135
+
136
+ ```astro
137
+ ---
138
+ import { NotroContent, getPlainText } from "notro-loader";
139
+ import { notroComponents } from "@/components/notro";
140
+
141
+ const { entry } = Astro.props;
142
+ ---
143
+
144
+ <NotroContent markdown={entry.data.markdown} components={notroComponents} />
145
+ ```
146
+
147
+ Components are copied into `src/components/notro/` so you can edit them directly.
148
+
149
+ ## Markdown processing (remark-nfm)
150
+
151
+ `notro-loader` delegates Notion Markdown preprocessing and directive syntax support to the [`remark-nfm`](https://www.npmjs.com/package/remark-nfm) package.
152
+
153
+ `remark-nfm` is used inside notro-loader's MDX compile pipeline and is applied automatically when using `NotroContent`.
154
+
155
+ If you want to use `remark-nfm` directly (in a custom unified pipeline or `@mdx-js/mdx`'s `evaluate()`), import it from the `remark-nfm` package directly rather than from `notro-loader`.
156
+
157
+ ```ts
158
+ // ✅ Import directly from remark-nfm
159
+ import { remarkNfm, preprocessNotionMarkdown } from "remark-nfm";
160
+
161
+ // ❌ Not needed from notro-loader (internal use only)
162
+ // import { remarkNfm } from "notro-loader";
163
+ ```
164
+
165
+ ## Notion API limitations
166
+
167
+ > Reference: [Retrieve a page as Markdown – Notion API](https://developers.notion.com/reference/retrieve-page-markdown)
168
+
169
+ ### Content truncation (`truncated`)
170
+
171
+ `GET /v1/pages/{page_id}/markdown` truncates content at approximately **20,000 blocks**.
172
+
173
+ - Detectable via `truncated: true` in the response, but **there is no pagination API to fetch the rest**
174
+ - notro logs a warning when `truncated === true` and continues the build with the available content
175
+ - Workaround: split large Notion pages into multiple smaller pages
176
+
177
+ ```
178
+ ⚠ Page abc123: markdown content was truncated by the Notion API (~20,000 block limit).
179
+ No pagination is available for this endpoint.
180
+ Consider splitting this Notion page into smaller pages to avoid truncation.
181
+ ```
182
+
183
+ ### Unrenderable blocks (`unknown_block_ids`)
184
+
185
+ `unknown_block_ids` in the response lists block IDs that the Notion API could not convert to Markdown (unsupported block types, etc.).
186
+
187
+ - These blocks are **silently omitted** from the `markdown` field
188
+ - There is no way to retrieve their content via this endpoint
189
+ - notro logs the block IDs as a warning and continues the build
190
+
191
+ ```
192
+ ⚠ Page abc123: 2 block(s) could not be rendered to Markdown by the Notion API and were omitted.
193
+ Block IDs: xxxxxxxx-..., yyyyyyyy-...
194
+ ```
195
+
196
+ ### API errors and automatic retries
197
+
198
+ | Error | Handling |
199
+ |---|---|
200
+ | `429 rate_limited` / `500 internal_server_error` / `503 service_unavailable` | Retry with exponential backoff (1s / 2s / 4s, up to 3 times) |
201
+ | `401 unauthorized` / `403 restricted_resource` / `404 object_not_found` | No retry. Logs a warning and skips the page |
202
+ | Other unexpected errors | Logs a warning and skips the page (build continues) |
203
+
204
+ ## Environment variables
205
+
206
+ | Variable | Description |
207
+ |---|---|
208
+ | `NOTION_TOKEN` | Notion Internal Integration Token |
209
+ | `NOTION_DATASOURCE_ID` | Notion data source ID |
210
+
211
+ ## API Reference
212
+
213
+ ### `loader(options)`
214
+
215
+ Astro Content Loader. Pass Notion API `dataSources.query` parameters via `queryParameters`.
216
+
217
+ ### `pageWithMarkdownSchema`
218
+
219
+ Base Zod schema returned by the loader. Extends `pageObjectResponseSchema` with `markdown: z.string()`. Extend with `.extend()` for custom schemas.
220
+
221
+ ### Property schemas
222
+
223
+ Use the `notroProperties` shorthand to define database property types in `content.config.ts` (see [`notroProperties`](#notroproperties)).
224
+
225
+ Individual schemas (e.g. `titlePropertyPageObjectResponseSchema`) remain exported for backwards compatibility.
226
+
227
+ ### Components
228
+
229
+ | Component | Description |
230
+ |---|---|
231
+ | `NotroContent` | Renders Notion Markdown to HTML. Unstyled by default; pass `components` to customize |
232
+ | `DatabaseCover` | Renders a Notion cover image with optimization |
233
+ | `DatabaseProperty` | Renders a Notion property value by type |
234
+ | `compileMdxCached` | Low-level MDX compile API. Use when building a custom `NotroContent` |
235
+
236
+ ### `notroProperties`
237
+
238
+ Zod schema shorthands for defining property schemas in `content.config.ts`. Each key maps to a Notion property type.
239
+
240
+ ```typescript
241
+ import { notroProperties } from "notro-loader";
242
+
243
+ // notroProperties.title → titlePropertyPageObjectResponseSchema
244
+ // notroProperties.richText → richTextPropertyPageObjectResponseSchema
245
+ // notroProperties.checkbox → checkboxPropertyPageObjectResponseSchema
246
+ // notroProperties.multiSelect → multiSelectPropertyPageObjectResponseSchema
247
+ // notroProperties.select → selectPropertyPageObjectResponseSchema
248
+ // notroProperties.date → datePropertyPageObjectResponseSchema
249
+ // notroProperties.number → numberPropertyPageObjectResponseSchema
250
+ // notroProperties.url → urlPropertyPageObjectResponseSchema
251
+ // notroProperties.email → emailPropertyPageObjectResponseSchema
252
+ // notroProperties.phoneNumber → phoneNumberPropertyPageObjectResponseSchema
253
+ // notroProperties.files → filesPropertyPageObjectResponseSchema
254
+ // notroProperties.people → peoplePropertyPageObjectResponseSchema
255
+ // notroProperties.relation → relationPropertyPageObjectResponseSchema
256
+ // notroProperties.rollup → rollupPropertyPageObjectResponseSchema
257
+ // notroProperties.formula → formulaPropertyPageObjectResponseSchema
258
+ // notroProperties.uniqueId → uniqueIdPropertyPageObjectResponseSchema
259
+ // notroProperties.status → statusPropertyPageObjectResponseSchema
260
+ // notroProperties.createdTime → createdTimePropertyPageObjectResponseSchema
261
+ // notroProperties.createdBy → createdByPropertyPageObjectResponseSchema
262
+ // notroProperties.lastEditedTime → lastEditedTimePropertyPageObjectResponseSchema
263
+ // notroProperties.lastEditedBy → lastEditedByPropertyPageObjectResponseSchema
264
+ // notroProperties.button → buttonPropertyPageObjectResponseSchema
265
+ // notroProperties.verification → verificationPropertyPageObjectResponseSchema
266
+ ```
267
+
268
+ ### Utilities
269
+
270
+ | Function | Description |
271
+ |---|---|
272
+ | `getPlainText(property)` | Extracts plain text from Title, Rich Text, Select, Multi-select, Number, URL, Email, Phone, Date, and Unique ID properties |
273
+ | `getMultiSelect(property)` | Returns the options array for a multi-select property. Returns an empty array for unsupported types or `undefined` — no type guard needed |
274
+ | `hasTag(property, tagName)` | Returns whether a multi-select property contains the given tag name. Safe to call without a type guard |
275
+ | `buildLinkToPages(entries, options)` | Builds a `linkToPages` map from collection entries. Pass to `NotroContent` for resolving inter-page Notion links |
276
+ | `colorToCSS(color)` | Converts a Notion color name to an inline CSS style string (for use in custom components) |