@draftbase/renderer 0.1.0 → 0.1.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/README.md CHANGED
@@ -1,6 +1,23 @@
1
+ <div align="center">
2
+
1
3
  # @draftbase/renderer
2
4
 
3
- Framework-agnostic MDX renderer for [Draftbase](https://draftbase.co), the MDX-based headless CMS for React developers. Takes an entry's MDX/markdown field and renders it into a real component tree (React or Vue) or a plain HTML string — no vendor lock-in to one frontend framework. `compileMDX` is a plain async function with no dependency on Next.js, a bundler, or a router.
5
+ **Framework-agnostic MDX renderer for [Draftbase](https://draftbase.co)** the MDX-based headless CMS for React developers.
6
+
7
+ [![npm](https://img.shields.io/npm/v/@draftbase/renderer)](https://www.npmjs.com/package/@draftbase/renderer)
8
+ [![GitHub](https://img.shields.io/badge/GitHub-renderer-181717?logo=github)](https://github.com/draftbase-co/renderer)
9
+
10
+ </div>
11
+
12
+ Takes an entry's MDX/markdown field and renders it into a real component tree (React or Vue) or a plain HTML string — no vendor lock-in to one frontend framework. `compileMDX` is a plain async function with no dependency on Next.js, a bundler, or a router.
13
+
14
+ ## 📦 Install
15
+
16
+ ```bash
17
+ pnpm add @draftbase/renderer
18
+ ```
19
+
20
+ ## 🧭 Pick your entry point
4
21
 
5
22
  Every framework entry point exposes the **same API shape** — `compileMDX(source)` resolving to `{ ok: true, Content }` or `{ ok: false, error }` — so switching frameworks (or supporting several in one monorepo) means changing the import path, not the calling code:
6
23
 
@@ -12,13 +29,7 @@ Every framework entry point exposes the **same API shape** — `compileMDX(sourc
12
29
 
13
30
  Only import the entry point for the framework you use — each pulls in just that framework's peer dependency (React or Vue), never both, so an app using one never bundles code for the other.
14
31
 
15
- ## Install
16
-
17
- ```bash
18
- pnpm add @draftbase/renderer
19
- ```
20
-
21
- ## Next.js App Router
32
+ ## ⚛️ Next.js App Router
22
33
 
23
34
  ```tsx
24
35
  import { MDXContent } from "@draftbase/renderer";
@@ -61,6 +72,16 @@ function Entry({ source }: { source: string }) {
61
72
  }
62
73
  ```
63
74
 
75
+ `compiled.ok` only catches MDX _syntax_ errors. If the source references a JSX component you didn't pass in `components` (e.g. `<Callout>` without a `Callout` implementation), React throws while rendering `<Content>` — wrap it in the exported `MDXErrorBoundary` (React only, not React Native's non-DOM tree unless you supply an `errorTag`-equivalent fallback) to log it to the console and fail soft instead of crashing the page. `MDXContent` (the Next.js RSC helper above) already does this for you automatically.
76
+
77
+ ```tsx
78
+ import { MDXErrorBoundary } from "@draftbase/renderer";
79
+
80
+ <MDXErrorBoundary fallback={<Text>{source}</Text>}>
81
+ <Content components={{ p: Text, h1: Text /* ... */ }} />
82
+ </MDXErrorBoundary>;
83
+ ```
84
+
64
85
  Extended markdown (tables, strikethrough, task lists, autolinks) is supported out of the box via `remark-gfm`.
65
86
 
66
87
  ## Astro
@@ -181,6 +202,27 @@ import { Callout, ImageBlock } from "@/components/content";
181
202
 
182
203
  Any standard markdown element (`h1`, `table`, `a`, ...) can also be overridden the same way, by key — **required** on non-DOM renderers like React Native, which have no intrinsic `div`/`p`/`a`/`img` tags.
183
204
 
205
+ ### Entry links
206
+
207
+ The Draftbase editor can insert `<EntryLink id="...">Link text</EntryLink>` into rich text to link to another entry. It's a plain JSX component like any other — no special renderer support for the tag itself — so you must supply an `EntryLink` implementation the same way as `Callout`/`ImageBlock`. If `EntryLink` isn't supplied and the source contains one, rendering throws (same as any missing custom component).
208
+
209
+ To route `id` correctly per content type, fetch the entry with `include=1` (via `@draftbase/sdk`) — the response includes an `entryLinks` map keyed by every `EntryLink` id found in that entry's richText fields, each with its `templateId`:
210
+
211
+ ```tsx
212
+ const entry = await client.entries.get(entryId, undefined, 1);
213
+ // entry.entryLinks = { "64f1a2b3c4d5e6f7a8b9c0d1": { id, templateId, title, status } }
214
+
215
+ const ROUTE_BY_TEMPLATE: Record<string, string> = { blogPost: "/blog", product: "/products" };
216
+
217
+ function EntryLink({ id, children }: { id: string; children: React.ReactNode }) {
218
+ const link = entry.entryLinks?.[id];
219
+ const base = ROUTE_BY_TEMPLATE[link?.templateId ?? ""] ?? "/entries";
220
+ return <a href={`${base}/${id}`}>{children}</a>;
221
+ }
222
+
223
+ <MDXContent source={entry.fields.body} components={{ EntryLink }} />;
224
+ ```
225
+
184
226
  ## Styling
185
227
 
186
228
  `@draftbase/renderer/styles.css` wraps output in a `.db-content` class with slim, sensible defaults (typography, tables, code blocks). Web-only, opt-in:
@@ -219,3 +261,15 @@ Yes — `compileMDX`/`toHtml` produce standard semantic HTML (headings, lists, t
219
261
 
220
262
  **Which frontend frameworks are supported?**
221
263
  React (Next.js App Router/RSC, plain client React, React Native, Remix, Astro islands, Vite) and Vue (including Nuxt) get first-class component output. Any other framework (Svelte, Angular, Solid, plain HTML/email/RSS) can use `toHtml` to get a plain HTML string instead.
264
+
265
+ ## Links
266
+
267
+ - [npm](https://www.npmjs.com/package/@draftbase/renderer)
268
+ - [Source](https://github.com/draftbase-co/renderer)
269
+ - [Issues](https://github.com/draftbase-co/renderer/issues)
270
+ - [`@draftbase/sdk`](https://www.npmjs.com/package/@draftbase/sdk) — fetches the content this package renders
271
+ - [draftbase.co](https://draftbase.co) — product site
272
+ - [Framework support](https://draftbase.co/frameworks) — per-framework rendering guide this README is based on
273
+ - [API reference](https://draftbase.co/docs/api-reference)
274
+ - [Docs](https://draftbase.co/docs)
275
+ - [Pricing](https://draftbase.co/pricing)
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as runtime from "react/jsx-runtime";
3
3
  import { compileMDXCore } from "./core.js";
4
4
  import { wrapperClassName } from "./wrapperClassName.js";
5
+ import { MDXErrorBoundary } from "./MDXErrorBoundary.js";
5
6
  /**
6
7
  * Compiles raw MDX/markdown into a renderable React component — no DOM assumptions,
7
8
  * safe to call from a React Native loader, a client-side effect, or a Next.js Server
@@ -27,5 +28,5 @@ export async function MDXContent({ source, components, unstyled, className, wrap
27
28
  return (_jsx(Wrapper, { className: wrapperClass, children: _jsx(ErrorTag, { children: source }) }));
28
29
  }
29
30
  const { Content } = compiled;
30
- return (_jsx(Wrapper, { className: wrapperClass, children: _jsx(Content, { components: components }) }));
31
+ return (_jsx(Wrapper, { className: wrapperClass, children: _jsx(MDXErrorBoundary, { fallback: _jsx(ErrorTag, { children: source }), children: _jsx(Content, { components: components }) }) }));
31
32
  }
@@ -0,0 +1,17 @@
1
+ import { Component, type ReactNode } from "react";
2
+ interface MDXErrorBoundaryProps {
3
+ children: ReactNode;
4
+ fallback: ReactNode;
5
+ }
6
+ interface MDXErrorBoundaryState {
7
+ hasError: boolean;
8
+ }
9
+ export declare class MDXErrorBoundary extends Component<MDXErrorBoundaryProps, MDXErrorBoundaryState> {
10
+ state: MDXErrorBoundaryState;
11
+ static getDerivedStateFromError(): {
12
+ hasError: boolean;
13
+ };
14
+ componentDidCatch(error: unknown): void;
15
+ render(): ReactNode;
16
+ }
17
+ export {};
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { Component } from "react";
3
+ // Catches render-time errors from compiled MDX content — most commonly a JSX component
4
+ // referenced in the source (e.g. `<Callout>`, `<EntryLink>`) that wasn't supplied in
5
+ // `components`. Without this, that throws "Element type is invalid" and crashes the whole
6
+ // page instead of just the content block.
7
+ export class MDXErrorBoundary extends Component {
8
+ state = { hasError: false };
9
+ static getDerivedStateFromError() {
10
+ return { hasError: true };
11
+ }
12
+ componentDidCatch(error) {
13
+ console.error("MDX content failed to render", error);
14
+ }
15
+ render() {
16
+ return this.state.hasError ? this.props.fallback : this.props.children;
17
+ }
18
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { MDXContent, compileMDX } from "./MDXContent.js";
2
2
  export type { MDXContentProps, CompiledMDX, FailedMDX } from "./MDXContent.js";
3
3
  export { toHtml } from "./toHtml.js";
4
+ export { MDXErrorBoundary } from "./MDXErrorBoundary.js";
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { MDXContent, compileMDX } from "./MDXContent.js";
2
2
  export { toHtml } from "./toHtml.js";
3
+ export { MDXErrorBoundary } from "./MDXErrorBoundary.js";
package/dist/styles.css CHANGED
@@ -25,6 +25,12 @@
25
25
  line-height: 1.25;
26
26
  margin: 1.5em 0 0.5em;
27
27
  color: inherit;
28
+ scroll-margin-top: 96px;
29
+ }
30
+
31
+ /* Highlights a footnote/citation anchor when the URL hash lands on it, e.g. #fn-1. */
32
+ .db-content :target {
33
+ background: var(--accent-subtle, rgba(127, 127, 127, 0.15));
28
34
  }
29
35
 
30
36
  .db-content h1 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftbase/renderer",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Framework-agnostic MDX renderer for Draftbase content (React, Next.js RSC, React Native, Astro, Vite, Vue)",
5
5
  "keywords": [
6
6
  "draftbase",
@@ -17,8 +17,7 @@
17
17
  "homepage": "https://draftbase.co",
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "git+https://github.com/draftbase-co/draftbase-monorepo.git",
21
- "directory": "packages/renderer"
20
+ "url": "git+https://github.com/draftbase-co/renderer.git"
22
21
  },
23
22
  "publishConfig": {
24
23
  "access": "public"
@@ -39,6 +38,11 @@
39
38
  "LICENSE",
40
39
  "README.md"
41
40
  ],
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.json && cp src/styles.css dist/styles.css",
43
+ "lint": "tsc --noEmit",
44
+ "test": "node --test dist/wrapperClassName.test.js dist/toHtml.test.js dist/compileMDX.test.js"
45
+ },
42
46
  "peerDependencies": {
43
47
  "react": ">=18",
44
48
  "vue": ">=3"
@@ -67,10 +71,5 @@
67
71
  "react": "^19.0.0",
68
72
  "vue": "^3.5.0",
69
73
  "typescript": "^5.6.0"
70
- },
71
- "scripts": {
72
- "build": "tsc -p tsconfig.json && cp src/styles.css dist/styles.css",
73
- "lint": "tsc --noEmit",
74
- "test": "node --test dist/wrapperClassName.test.js dist/toHtml.test.js dist/compileMDX.test.js"
75
74
  }
76
- }
75
+ }