react-email-studio 2.0.0 → 3.1.1

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/TUTORIAL.md CHANGED
@@ -1,264 +1,264 @@
1
- # react-email-studio — Integration tutorial & API reference
2
-
3
- This document is for **npm users**: wiring **react-email-studio** into an app, persisting designs, generating HTML, and using optional APIs (preview modal, helpers, TypeScript types).
4
-
5
- ---
6
-
7
- ## What you are integrating
8
-
9
- | Piece | Role |
10
- |--------|------|
11
- | **`ReactEmailEditor`** | Full-screen (or sized) visual editor: rows, columns, blocks, settings. |
12
- | **Design JSON** | Canonical storage format: `email_document` schema (see exported types). |
13
- | **`jsonToHtml`** | Turns saved JSON into a **full HTML document** for preview iframes or your ESP. |
14
- | **`onUpload`** | Your code uploads assets and returns **HTTPS URLs** referenced in the HTML. |
15
-
16
- The package does **not** send mail, host files, or provide a backend—you connect those.
17
-
18
- ---
19
-
20
- ## 1. Install
21
-
22
- ```bash
23
- npm install react-email-studio
24
- ```
25
-
26
- Install **peer dependencies** in your app (same major versions as in this package’s `peerDependencies`):
27
-
28
- - `react`, `react-dom`
29
- - `lucide-react`
30
- - TipTap v3: `@tiptap/react`, `@tiptap/core`, `@tiptap/starter-kit`, `@tiptap/extension-link`, `@tiptap/extension-placeholder`, `@tiptap/extension-text-align`, `@tiptap/extension-text-style`, `@tiptap/extension-underline`
31
-
32
- TypeScript projects should also have **`@types/react`** and **`@types/react-dom`** as dev dependencies.
33
-
34
- ---
35
-
36
- ## 2. Minimal integration (save / load)
37
-
38
- ```tsx
39
- import { useRef, useCallback } from "react";
40
- import {
41
- ReactEmailEditor,
42
- type ReactEmailEditorRef,
43
- jsonToHtml,
44
- } from "react-email-studio";
45
-
46
- export function MailStudioPage() {
47
- const ref = useRef<ReactEmailEditorRef>(null);
48
-
49
- const save = useCallback(() => {
50
- ref.current?.exportJson((jsonString, _pretty) => {
51
- // POST jsonString to your API or write to localStorage
52
- void fetch("/api/email-designs", {
53
- method: "POST",
54
- headers: { "Content-Type": "application/json" },
55
- body: JSON.stringify({ design: jsonString }),
56
- });
57
- }, true);
58
- }, []);
59
-
60
- return (
61
- <>
62
- <button type="button" onClick={save}>
63
- Save design
64
- </button>
65
- <ReactEmailEditor
66
- ref={ref}
67
- hideTemplates
68
- onReady={(api) => {
69
- // Load previously saved JSON (string or parsed object)
70
- // const saved = await fetch(...).then(r => r.json());
71
- // api.loadJson(saved.design);
72
- }}
73
- onUpload={uploadImageAndReturnUrl}
74
- options={{ locale: "en" }}
75
- />
76
- </>
77
- );
78
- }
79
-
80
- async function uploadImageAndReturnUrl(file: File): Promise<string> {
81
- const body = new FormData();
82
- body.append("file", file);
83
- const res = await fetch("/api/upload", { method: "POST", body });
84
- const { url } = await res.json();
85
- return url as string;
86
- }
87
-
88
- // Server-side or after save: build HTML for sending
89
- function htmlFromStoredDesign(designJson: string) {
90
- return jsonToHtml(designJson, {
91
- customCSS: `/* optional: brand overrides */`,
92
- });
93
- }
94
- ```
95
-
96
- **Important:** `onUpload` must return a URL that **email recipients** can open (usually `https://…`). Avoid `localhost` or short-lived signed URLs unless you understand deliverability.
97
-
98
- ---
99
-
100
- ## 3. Framework notes
101
-
102
- ### Vite / Create React App
103
-
104
- Import the editor like any other component. No special config is required beyond installing peers.
105
-
106
- ### Next.js (App Router)
107
-
108
- The editor depends on the DOM and TipTap. Use a **client** boundary:
109
-
110
- ```tsx
111
- "use client";
112
-
113
- import { ReactEmailEditor } from "react-email-studio";
114
- // ...
115
- ```
116
-
117
- If you see SSR-related errors (window/document during prerender), load the editor only on the client:
118
-
119
- ```tsx
120
- import dynamic from "next/dynamic";
121
-
122
- const ReactEmailEditor = dynamic(
123
- () => import("react-email-studio").then((m) => m.ReactEmailEditor),
124
- { ssr: false },
125
- );
126
- ```
127
-
128
- Adjust the import path to match your structure.
129
-
130
- ---
131
-
132
- ## 4. `ReactEmailEditor` — props
133
-
134
- | Prop | Type | Description |
135
- |------|------|-------------|
136
- | `ref` | `ReactEmailEditorRef` | Imperative API: `loadJson`, `exportJson` (see below). |
137
- | `options` | `ReactEmailEditorOptions` | Editor chrome, locale, merge tags, feature flags, etc. (see §6). |
138
- | `onUpload` | `(file: File) => Promise<string>` | Required for good image/video UX; return public URL string. |
139
- | `onLoad` / `onReady` | `(api: ReactEmailEditorRef) => void` | Fired once with the same API object; use to call `loadJson` with server data. |
140
- | `hideTemplates` | `boolean` | Hide built-in templates UI; use when you only load via `loadJson`. |
141
- | `templates` | `{ name: string; design: unknown; thumbnail?: string }[]` | Optional starter templates. |
142
- | `minHeight` | `string` | Default `"100vh"`. |
143
- | `editorId` | `string` | Prefix for DOM ids (multiple editors on one page). |
144
- | `style` | `CSSProperties` | Outer wrapper style. |
145
-
146
- ---
147
-
148
- ## 5. Imperative API (`ReactEmailEditorRef`)
149
-
150
- | Method | Description |
151
- |--------|-------------|
152
- | `loadJson(input: string \| Record<string, unknown>)` | Replace editor content from JSON string or object (invalid input is ignored safely). |
153
- | `exportJson(cb: (json: string) => void, pretty?: boolean)` | Current design as JSON string (`email_document`). Use `pretty === true` for readable storage diffs. |
154
-
155
- ---
156
-
157
- ## 6. `options` (`ReactEmailEditorOptions`)
158
-
159
- `ReactEmailEditorOptions` extends HTML generation hints with **`customCSS`** (injected in preview HTML) plus editor behavior:
160
-
161
- | Area | Examples |
162
- |------|----------|
163
- | **`customCSS`** | Extra `<style>` content for generated HTML (preview / export parity). |
164
- | **`appearance`** | `theme`, `colors`, `customThemes` — chrome theming. |
165
- | **`locale`** | `"en"` \| `"fr"` \| `"de"` \| `"es"` for UI strings. |
166
- | **`mergeTags`** | `{ name: string; value: string }[]` for rich-text insertion. |
167
- | **`features.autoSave`** | `{ enabled?: boolean; interval?: number }` — UI autosave hint. |
168
- | **`tools`** | `{ [blockType: string]: { enabled?: boolean } }` — toggle blocks. |
169
- | **`blockLibrary`** | `groupHeadings`, `paletteGroupLabels` — library copy overrides. |
170
-
171
- Full typings ship with the package (`ReactEmailEditorOptions`).
172
-
173
- ---
174
-
175
- ## 7. `jsonToHtml` — HTML for preview & ESP
176
-
177
- ```ts
178
- import { jsonToHtml, type JsonToHtmlOptions } from "react-email-studio";
179
-
180
- const html: string = jsonToHtml(designInput, options);
181
- ```
182
-
183
- | Argument | Description |
184
- |----------|-------------|
185
- | `designInput` | `unknown` — JSON string or parsed object; invalid or empty designs yield `""`. |
186
- | `options` | `JsonToHtmlOptions` (= `EmailHtmlOptions`): `{ customCSS?: string }`. |
187
-
188
- Use the returned string as the **email body HTML** with your provider (SendGrid, SES, Mailgun, Postmark, …). Test in real clients (Gmail, Outlook, Apple Mail).
189
-
190
- ---
191
-
192
- ## 8. Optional: `EmailPreviewModal` & `emailPreviewDevices`
193
-
194
- For a **standalone** responsive preview (outside the built-in toolbar preview), the package exports:
195
-
196
- - **`EmailPreviewModal`** — modal with device frames.
197
- - **`emailPreviewDevices`** — device metadata for labels/layout.
198
-
199
- You must pass **`html`** (from `jsonToHtml` or your pipeline) and a theme map **`C`** (`Record<string, string>`) for chrome colors—mirror the palette you use in your app or define a minimal set for the modal.
200
-
201
- See TypeScript types **`EmailPreviewModalProps`** and **`MobilePreviewVariant`** (`"iphone"` \| `"android"`).
202
-
203
- ---
204
-
205
- ## 9. Encoding helpers
206
-
207
- | Export | Purpose |
208
- |--------|---------|
209
- | `utf8ToBase64(raw: string)` | Encode UTF-8 text to Base64. |
210
- | `base64ToUtf8(b64: string)` | Decode Base64 to UTF-8 string. |
211
-
212
- Useful for data URLs or compact transport—not email-specific.
213
-
214
- ---
215
-
216
- ## 10. TypeScript — exported types
217
-
218
- Import names match what you get from the package entry:
219
-
220
- | Type | Purpose |
221
- |------|---------|
222
- | `ReactEmailEditorProps`, `ReactEmailEditorRef`, `ReactEmailEditorOptions` | Component contract. |
223
- | `JsonToHtmlOptions`, `EmailHtmlOptions` | HTML generation options (`customCSS`). |
224
- | `EmailDocument`, `EmailDocumentSettings`, `EmailDocumentRow`, `EmailDocumentColumn`, `BlockBase` | JSON schema shapes for storage / validation. |
225
- | `EmailPreviewModalProps`, `MobilePreviewVariant` | Preview modal. |
226
-
227
- Example:
228
-
229
- ```ts
230
- import type {
231
- EmailDocument,
232
- ReactEmailEditorRef,
233
- } from "react-email-studio";
234
- ```
235
-
236
- ---
237
-
238
- ## 11. JSON schema (`email_document`)
239
-
240
- Persist **`exportJson`** output as your source of truth. Top-level shape:
241
-
242
- - `type: "email_document"`
243
- - `settings` — global email / content shell options
244
- - `rows` — layout rows → columns → blocks
245
-
246
- Use exported **`EmailDocument`** when typing API payloads or DB columns.
247
-
248
- ---
249
-
250
- ## 12. Troubleshooting
251
-
252
- | Issue | What to check |
253
- |-------|----------------|
254
- | Module not found (TipTap / lucide) | Install all peer dependencies; TipTap majors must align (v3). |
255
- | Blank HTML from `jsonToHtml` | Empty rows after normalization, or invalid JSON. |
256
- | Broken images in sent mail | URLs from `onUpload` must be publicly reachable from recipient networks. |
257
- | SSR errors in Next.js | `"use client"` and/or `dynamic(..., { ssr: false })`. |
258
-
259
- ---
260
-
261
-
262
- ## License
263
-
264
- MIT — see `package.json`.
1
+ # react-email-studio — Integration tutorial & API reference
2
+
3
+ This document is for **npm users**: wiring **react-email-studio** into an app, persisting designs, generating HTML, and using optional APIs (preview modal, helpers, TypeScript types).
4
+
5
+ ---
6
+
7
+ ## What you are integrating
8
+
9
+ | Piece | Role |
10
+ |--------|------|
11
+ | **`ReactEmailEditor`** | Full-screen (or sized) visual editor: rows, columns, blocks, settings. |
12
+ | **Design JSON** | Canonical storage format: `email_document` schema (see exported types). |
13
+ | **`jsonToHtml`** | Turns saved JSON into a **full HTML document** for preview iframes or your ESP. |
14
+ | **`onUpload`** | Your code uploads assets and returns **HTTPS URLs** referenced in the HTML. |
15
+
16
+ The package does **not** send mail, host files, or provide a backend—you connect those.
17
+
18
+ ---
19
+
20
+ ## 1. Install
21
+
22
+ ```bash
23
+ npm install react-email-studio
24
+ ```
25
+
26
+ Install **peer dependencies** in your app (same major versions as in this package’s `peerDependencies`):
27
+
28
+ - `react`, `react-dom`
29
+ - `lucide-react`
30
+ - TipTap v3: `@tiptap/react`, `@tiptap/core`, `@tiptap/starter-kit`, `@tiptap/extension-link`, `@tiptap/extension-placeholder`, `@tiptap/extension-text-align`, `@tiptap/extension-text-style`, `@tiptap/extension-underline`
31
+
32
+ TypeScript projects should also have **`@types/react`** and **`@types/react-dom`** as dev dependencies.
33
+
34
+ ---
35
+
36
+ ## 2. Minimal integration (save / load)
37
+
38
+ ```tsx
39
+ import { useRef, useCallback } from "react";
40
+ import {
41
+ ReactEmailEditor,
42
+ type ReactEmailEditorRef,
43
+ jsonToHtml,
44
+ } from "react-email-studio";
45
+
46
+ export function MailStudioPage() {
47
+ const ref = useRef<ReactEmailEditorRef>(null);
48
+
49
+ const save = useCallback(() => {
50
+ ref.current?.exportJson((jsonString, _pretty) => {
51
+ // POST jsonString to your API or write to localStorage
52
+ void fetch("/api/email-designs", {
53
+ method: "POST",
54
+ headers: { "Content-Type": "application/json" },
55
+ body: JSON.stringify({ design: jsonString }),
56
+ });
57
+ }, true);
58
+ }, []);
59
+
60
+ return (
61
+ <>
62
+ <button type="button" onClick={save}>
63
+ Save design
64
+ </button>
65
+ <ReactEmailEditor
66
+ ref={ref}
67
+ hideTemplates
68
+ onReady={(api) => {
69
+ // Load previously saved JSON (string or parsed object)
70
+ // const saved = await fetch(...).then(r => r.json());
71
+ // api.loadJson(saved.design);
72
+ }}
73
+ onUpload={uploadImageAndReturnUrl}
74
+ options={{ locale: "en" }}
75
+ />
76
+ </>
77
+ );
78
+ }
79
+
80
+ async function uploadImageAndReturnUrl(file: File): Promise<string> {
81
+ const body = new FormData();
82
+ body.append("file", file);
83
+ const res = await fetch("/api/upload", { method: "POST", body });
84
+ const { url } = await res.json();
85
+ return url as string;
86
+ }
87
+
88
+ // Server-side or after save: build HTML for sending
89
+ function htmlFromStoredDesign(designJson: string) {
90
+ return jsonToHtml(designJson, {
91
+ customCSS: `/* optional: brand overrides */`,
92
+ });
93
+ }
94
+ ```
95
+
96
+ **Important:** `onUpload` must return a URL that **email recipients** can open (usually `https://…`). Avoid `localhost` or short-lived signed URLs unless you understand deliverability.
97
+
98
+ ---
99
+
100
+ ## 3. Framework notes
101
+
102
+ ### Vite / Create React App
103
+
104
+ Import the editor like any other component. No special config is required beyond installing peers.
105
+
106
+ ### Next.js (App Router)
107
+
108
+ The editor depends on the DOM and TipTap. Use a **client** boundary:
109
+
110
+ ```tsx
111
+ "use client";
112
+
113
+ import { ReactEmailEditor } from "react-email-studio";
114
+ // ...
115
+ ```
116
+
117
+ If you see SSR-related errors (window/document during prerender), load the editor only on the client:
118
+
119
+ ```tsx
120
+ import dynamic from "next/dynamic";
121
+
122
+ const ReactEmailEditor = dynamic(
123
+ () => import("react-email-studio").then((m) => m.ReactEmailEditor),
124
+ { ssr: false },
125
+ );
126
+ ```
127
+
128
+ Adjust the import path to match your structure.
129
+
130
+ ---
131
+
132
+ ## 4. `ReactEmailEditor` — props
133
+
134
+ | Prop | Type | Description |
135
+ |------|------|-------------|
136
+ | `ref` | `ReactEmailEditorRef` | Imperative API: `loadJson`, `exportJson` (see below). |
137
+ | `options` | `ReactEmailEditorOptions` | Editor chrome, locale, merge tags, feature flags, etc. (see §6). |
138
+ | `onUpload` | `(file: File) => Promise<string>` | Required for good image/video UX; return public URL string. |
139
+ | `onLoad` / `onReady` | `(api: ReactEmailEditorRef) => void` | Fired once with the same API object; use to call `loadJson` with server data. |
140
+ | `hideTemplates` | `boolean` | Hide built-in templates UI; use when you only load via `loadJson`. |
141
+ | `templates` | `{ name: string; design: unknown; thumbnail?: string }[]` | Optional starter templates. |
142
+ | `minHeight` | `string` | Default `"100vh"`. |
143
+ | `editorId` | `string` | Prefix for DOM ids (multiple editors on one page). |
144
+ | `style` | `CSSProperties` | Outer wrapper style. |
145
+
146
+ ---
147
+
148
+ ## 5. Imperative API (`ReactEmailEditorRef`)
149
+
150
+ | Method | Description |
151
+ |--------|-------------|
152
+ | `loadJson(input: string \| Record<string, unknown>)` | Replace editor content from JSON string or object (invalid input is ignored safely). |
153
+ | `exportJson(cb: (json: string) => void, pretty?: boolean)` | Current design as JSON string (`email_document`). Use `pretty === true` for readable storage diffs. |
154
+
155
+ ---
156
+
157
+ ## 6. `options` (`ReactEmailEditorOptions`)
158
+
159
+ `ReactEmailEditorOptions` extends HTML generation hints with **`customCSS`** (injected in preview HTML) plus editor behavior:
160
+
161
+ | Area | Examples |
162
+ |------|----------|
163
+ | **`customCSS`** | Extra `<style>` content for generated HTML (preview / export parity). |
164
+ | **`appearance`** | `theme`, `colors`, `customThemes` — chrome theming. |
165
+ | **`locale`** | `"en"` \| `"fr"` \| `"de"` \| `"es"` for UI strings. |
166
+ | **`mergeTags`** | `{ name: string; value: string }[]` for rich-text insertion. |
167
+ | **`features.autoSave`** | `{ enabled?: boolean; interval?: number }` — UI autosave hint. |
168
+ | **`tools`** | `{ [blockType: string]: { enabled?: boolean } }` — toggle blocks. |
169
+ | **`blockLibrary`** | `groupHeadings`, `paletteGroupLabels` — library copy overrides. |
170
+
171
+ Full typings ship with the package (`ReactEmailEditorOptions`).
172
+
173
+ ---
174
+
175
+ ## 7. `jsonToHtml` — HTML for preview & ESP
176
+
177
+ ```ts
178
+ import { jsonToHtml, type JsonToHtmlOptions } from "react-email-studio";
179
+
180
+ const html: string = jsonToHtml(designInput, options);
181
+ ```
182
+
183
+ | Argument | Description |
184
+ |----------|-------------|
185
+ | `designInput` | `unknown` — JSON string or parsed object; invalid or empty designs yield `""`. |
186
+ | `options` | `JsonToHtmlOptions` (= `EmailHtmlOptions`): `{ customCSS?: string }`. |
187
+
188
+ Use the returned string as the **email body HTML** with your provider (SendGrid, SES, Mailgun, Postmark, …). Test in real clients (Gmail, Outlook, Apple Mail).
189
+
190
+ ---
191
+
192
+ ## 8. Optional: `EmailPreviewModal` & `emailPreviewDevices`
193
+
194
+ For a **standalone** responsive preview (outside the built-in toolbar preview), the package exports:
195
+
196
+ - **`EmailPreviewModal`** — modal with device frames.
197
+ - **`emailPreviewDevices`** — device metadata for labels/layout.
198
+
199
+ You must pass **`html`** (from `jsonToHtml` or your pipeline) and a theme map **`C`** (`Record<string, string>`) for chrome colors—mirror the palette you use in your app or define a minimal set for the modal.
200
+
201
+ See TypeScript types **`EmailPreviewModalProps`** and **`MobilePreviewVariant`** (`"iphone"` \| `"android"`).
202
+
203
+ ---
204
+
205
+ ## 9. Encoding helpers
206
+
207
+ | Export | Purpose |
208
+ |--------|---------|
209
+ | `utf8ToBase64(raw: string)` | Encode UTF-8 text to Base64. |
210
+ | `base64ToUtf8(b64: string)` | Decode Base64 to UTF-8 string. |
211
+
212
+ Useful for data URLs or compact transport—not email-specific.
213
+
214
+ ---
215
+
216
+ ## 10. TypeScript — exported types
217
+
218
+ Import names match what you get from the package entry:
219
+
220
+ | Type | Purpose |
221
+ |------|---------|
222
+ | `ReactEmailEditorProps`, `ReactEmailEditorRef`, `ReactEmailEditorOptions` | Component contract. |
223
+ | `JsonToHtmlOptions`, `EmailHtmlOptions` | HTML generation options (`customCSS`). |
224
+ | `EmailDocument`, `EmailDocumentSettings`, `EmailDocumentRow`, `EmailDocumentColumn`, `BlockBase` | JSON schema shapes for storage / validation. |
225
+ | `EmailPreviewModalProps`, `MobilePreviewVariant` | Preview modal. |
226
+
227
+ Example:
228
+
229
+ ```ts
230
+ import type {
231
+ EmailDocument,
232
+ ReactEmailEditorRef,
233
+ } from "react-email-studio";
234
+ ```
235
+
236
+ ---
237
+
238
+ ## 11. JSON schema (`email_document`)
239
+
240
+ Persist **`exportJson`** output as your source of truth. Top-level shape:
241
+
242
+ - `type: "email_document"`
243
+ - `settings` — global email / content shell options
244
+ - `rows` — layout rows → columns → blocks
245
+
246
+ Use exported **`EmailDocument`** when typing API payloads or DB columns.
247
+
248
+ ---
249
+
250
+ ## 12. Troubleshooting
251
+
252
+ | Issue | What to check |
253
+ |-------|----------------|
254
+ | Module not found (TipTap / lucide) | Install all peer dependencies; TipTap majors must align (v3). |
255
+ | Blank HTML from `jsonToHtml` | Empty rows after normalization, or invalid JSON. |
256
+ | Broken images in sent mail | URLs from `onUpload` must be publicly reachable from recipient networks. |
257
+ | SSR errors in Next.js | `"use client"` and/or `dynamic(..., { ssr: false })`. |
258
+
259
+ ---
260
+
261
+
262
+ ## License
263
+
264
+ MIT — see `package.json`.
package/USER_README.md CHANGED
@@ -1,28 +1,28 @@
1
- # react-email-studio — User guide
2
-
3
- For **install, quick start, and export list**, see **`README.md`** on npm.
4
-
5
- For **step-by-step integration** (Next.js, APIs, TypeScript), see **`TUTORIAL.md`**.
6
-
7
- ## Editor areas
8
-
9
- | Area | Role |
10
- |------|------|
11
- | **Canvas** | Drag blocks, click to select rows/blocks, zoom, preview. |
12
- | **Right rail** | Block inspector, row settings, **Blocks** / **Settings** tabs. |
13
- | **Toolbar** | Undo/redo, templates (if enabled), preview. |
14
-
15
- ## Core APIs
16
-
17
- - **`ref.loadJson` / `ref.exportJson`** — load or save design JSON.
18
- - **`onUpload`** — return a public **HTTPS** URL for uploaded images.
19
- - **`jsonToHtml`** — design JSON → full HTML for sending.
20
-
21
- ## Troubleshooting
22
-
23
- | Issue | Check |
24
- |-------|--------|
25
- | Module not found | Install all **peer** packages (React, TipTap v3, `lucide-react`). |
26
- | Broken images in mail | `onUpload` URLs must be reachable by recipients (not `localhost`). |
27
-
28
- MIT — see `package.json`.
1
+ # react-email-studio — User guide
2
+
3
+ For **install, quick start, and export list**, see **`README.md`** on npm.
4
+
5
+ For **step-by-step integration** (Next.js, APIs, TypeScript), see **`TUTORIAL.md`**.
6
+
7
+ ## Editor areas
8
+
9
+ | Area | Role |
10
+ |------|------|
11
+ | **Canvas** | Drag blocks, click to select rows/blocks, zoom, preview. |
12
+ | **Right rail** | Block inspector, row settings, **Blocks** / **Settings** tabs. |
13
+ | **Toolbar** | Undo/redo, templates (if enabled), preview. |
14
+
15
+ ## Core APIs
16
+
17
+ - **`ref.loadJson` / `ref.exportJson`** — load or save design JSON.
18
+ - **`onUpload`** — return a public **HTTPS** URL for uploaded images.
19
+ - **`jsonToHtml`** — design JSON → full HTML for sending.
20
+
21
+ ## Troubleshooting
22
+
23
+ | Issue | Check |
24
+ |-------|--------|
25
+ | Module not found | Install all **peer** packages (React, TipTap v3, `lucide-react`). |
26
+ | Broken images in mail | `onUpload` URLs must be reachable by recipients (not `localhost`). |
27
+
28
+ MIT — see `package.json`.