From f7a4bb45dbafec01ec37a8d89198e109cb3a8fdf Mon Sep 17 00:00:00 2001 From: sidgaikwad Date: Fri, 4 Sep 2026 12:07:40 +0530 Subject: [PATCH 1/2] docs: document scriptUrl, the offline options, and the exported types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `scriptUrl` is fully implemented and tested but was absent from the props table, so the only supported way to pin an embed environment was undiscoverable outside the .d.ts. Add it, along with the doc comment's caveat about one embed winning per page. Also close the gaps around it: - `offline`, `licenseUrl` and `env` were named in the options row but never explained anywhere. Give them a section with a worked example. - Add a TypeScript usage example — every other example was JSX — and a table of the exported types. The TypeScript example is verified to compile against the real source types via the demo's tsconfig. --- README.md | 93 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index eafdcdb..49749c1 100644 --- a/README.md +++ b/README.md @@ -44,22 +44,63 @@ const App = () => { }; ``` +### TypeScript + +Every type is exported from the package root: + +```tsx +import { useRef } from 'react'; +import ImageEditor, { + type ImageEditorOptions, + type ImageEditorRef, + type ImageEditorSaveResult, +} from '@unlayer/react-image-editor'; + +const options: ImageEditorOptions = { theme: 'dark', locale: 'fr' }; + +export function Editor({ image }: { image: string }) { + const editorRef = useRef(null); + + const save = (result: ImageEditorSaveResult) => { + console.info(result.dataUrl, result.blob); + }; + + return ( + + ); +} +``` + +| Type | What it is | +| ----------------------- | ----------------------------------------------------------- | +| `ImageEditorProps` | The component's full prop type. | +| `ImageEditorOptions` | The `options` prop — everything the component does not own. | +| `ImageEditorRef` | The ref shape, `{ editor }`. | +| `ImageEditorInstance` | The editor instance and its methods. | +| `ImageEditorSaveResult` | `{ dataUrl, blob }`, passed to `onSave`. | + The component works out of the box in React Server Components environments (e.g. Next.js App Router) — it ships with the `'use client'` directive and touches the DOM only inside effects. ## Props -| Prop | Type | Description | -| ------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `image` | `string` (required) | Image URL or base64 data URL to edit. | -| `options` | `ImageEditorOptions` | Editor configuration: `projectId`, `user`, `features`, `theme`, `locale`, `translations`, `env`, `offline`, `licenseUrl`, `defaultPrompt`, `autoSubmitPrompt`, `aiAssistantOpenState`. | -| `editorId` | `string` | id for the container div. Cosmetic — the editor mounts by element reference. | -| `minHeight` | `number \| string` | Minimum height of the editor container. Defaults to `500`. | -| `style` | `CSSProperties` | Styles applied to the container div. | -| `onLoad` | `(editor) => void` | Called with the editor instance once it is mounted. | -| `onSave` | `({ dataUrl, blob }) => void` | Called when the user saves the edited image. | -| `onCancel` | `() => void` | Called when the user cancels editing. | -| `onLoadError` | `() => void` | Called when the image fails to load into the canvas (CORS, 404, decode error). | -| `onError` | `(error: Error) => void` | Wrapper-level failures: embed script load, editor creation, or image reset. Falls back to `console.error` when absent. | +| Prop | Type | Description | +| ------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `image` | `string` (required) | Image URL or base64 data URL to edit. | +| `options` | `ImageEditorOptions` | Editor configuration: `projectId`, `user`, `features`, `theme`, `locale`, `translations`, `env`, `offline`, `licenseUrl`, `defaultPrompt`, `autoSubmitPrompt`, `aiAssistantOpenState`. | +| `editorId` | `string` | id for the container div. Cosmetic — the editor mounts by element reference. | +| `minHeight` | `number \| string` | Minimum height of the editor container. Defaults to `500`. | +| `style` | `CSSProperties` | Styles applied to the container div. | +| `scriptUrl` | `string` | Override the embed script URL, e.g. to pin an environment. One embed per page: the first loader to run installs `window.ImageEditor` and wins globally, so do not mix different `scriptUrl`s across components. | +| `onLoad` | `(editor) => void` | Called with the editor instance once it is mounted. | +| `onSave` | `({ dataUrl, blob }) => void` | Called when the user saves the edited image. | +| `onCancel` | `() => void` | Called when the user cancels editing. | +| `onLoadError` | `() => void` | Called when the image fails to load into the canvas (CORS, 404, decode error). | +| `onError` | `(error: Error) => void` | Wrapper-level failures: embed script load, editor creation, or image reset. Falls back to `console.error` when absent. | ## Editor instance (ref) @@ -192,6 +233,34 @@ The editor includes an optional AI Assistant for chat-based edits. It requires a /> ``` +## Offline and self-hosted assets + +By default the editor talks to Unlayer's APIs and loads its assets (fonts, frames, stickers) from the CDN. Three options change that: + +| Option | Type | Purpose | +| ------------ | --------- | ------------------------------------------------------------------------------------------------------- | +| `offline` | `boolean` | Skips all external API calls. AI features are unavailable; entitlements come from `licenseUrl` instead. | +| `licenseUrl` | `string` | URL to the encrypted `license.json` used in offline mode to load entitlements. | +| `env` | `object` | Runtime overrides for base URLs. Takes precedence over build-time env vars. | + +```jsx + +``` + +`env` is a remount-tier option — set it before mounting rather than toggling it live. + ## Localization Set `options.locale` (bundled: `en`, `es`, `fr`, `de`, `it`, `pt`, `nl`, `ja`, `ko`, `zh`) and optionally override strings with `options.translations`. From 8165e919485516ebcd558e50b5b4ff706d86495f Mon Sep 17 00:00:00 2001 From: sidgaikwad Date: Wed, 9 Sep 2026 22:14:06 +0530 Subject: [PATCH 2/2] docs: separate self-hosted assets from offline mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The offline example combined offline: true with IMAGE_EDITOR_BASE_URL, but offline turns off URL-based asset loading, so that configuration silently produces an editor with no stickers, no frames and fallback text fonts. In image-editor 2.4.0 the asset resolver is assetMap?.[path] ?? bundled[path] ?? (!offline && base ? `${base}/assets/${path}` : '') with `bundled` empty in the CDN build, so with offline set and no injected map every asset resolves to ''. Confirmed in the browser: offline plus a valid IMAGE_EDITOR_BASE_URL gives 590 sticker elements with 590 empty src attributes; dropping offline alone takes that to 0. Split the section in two: - Self-hosting the editor assets — IMAGE_EDITOR_BASE_URL and scriptUrl, online, assets still resolved by URL. - Offline mode — what it disables, plus the three things a working setup needs: locally served scripts, an asset map via setAssetsMap on the image-editor-ready event, and self-hosted UI fonts, since the Google Fonts stylesheet is not injected when offline. --- README.md | 65 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 49749c1..d11fc5c 100644 --- a/README.md +++ b/README.md @@ -233,34 +233,73 @@ The editor includes an optional AI Assistant for chat-based edits. It requires a /> ``` -## Offline and self-hosted assets +## Self-hosting the editor assets -By default the editor talks to Unlayer's APIs and loads its assets (fonts, frames, stickers) from the CDN. Three options change that: - -| Option | Type | Purpose | -| ------------ | --------- | ------------------------------------------------------------------------------------------------------- | -| `offline` | `boolean` | Skips all external API calls. AI features are unavailable; entitlements come from `licenseUrl` instead. | -| `licenseUrl` | `string` | URL to the encrypted `license.json` used in offline mode to load entitlements. | -| `env` | `object` | Runtime overrides for base URLs. Takes precedence over build-time env vars. | +The editor loads its stickers, frames and text-tool fonts from the CDN, resolving each one against a base URL. Point that at your own copy with `env.IMAGE_EDITOR_BASE_URL`, and pin the embed script with `scriptUrl`: ```jsx `. + IMAGE_EDITOR_BASE_URL: '/vendor/image-editor', API_V2_BASE_URL: 'https://api.example.com/v2', - API_V3_BASE_URL: 'https://api.example.com/v3', + API_V3_BASE_URL: 'https://api.example.com/builder/v3', }, }} /> ``` +Copy the `assets/` directory from `https://cdn.unlayer.com/image-editor//` to that location. This keeps asset traffic on your own infrastructure while the editor still resolves assets **by URL** — it is not the same thing as offline mode, and the two are configured differently. + `env` is a remount-tier option — set it before mounting rather than toggling it live. +## Offline mode + +`offline: true` skips all external API calls. Entitlements come from `licenseUrl` instead of the API, and AI features are unavailable. + +> **`offline` also turns off URL-based asset loading.** Setting it together with `IMAGE_EDITOR_BASE_URL` does not give you self-hosted assets — the base URL is ignored for assets, every sticker and frame resolves to an empty string, and the text tool's fonts fall back. Offline mode needs an explicit asset map instead. + +A working offline setup has three parts: + +**1. Serve the scripts yourself.** Offline means no CDN, so host `embed.js` and the versioned bundle locally and point `scriptUrl` at your copy. + +**2. Provide an asset map.** The bundle exposes `setAssetsMap`, which takes an object keyed by the asset paths the editor asks for (`images/stickers/…`, `images/frames/…`, `fonts/…`) and valued with anything the browser can load — a local URL or a `data:` URI. It must be set before the editor renders the panels that use those assets; the embed fires `image-editor-ready` once the bundle has registered: + +```jsx +window.addEventListener( + 'image-editor-ready', + () => { + window.__ImageEditorImpl__.setAssetsMap({ + 'images/stickers/beach/0.svg': + '/vendor/image-editor/assets/images/stickers/beach/0.svg', + 'images/frames/art1/bottom.png': + '/vendor/image-editor/assets/images/frames/art1/bottom.png', + 'fonts/TrashHand.ttf': '/vendor/image-editor/assets/fonts/TrashHand.ttf', + // …one entry per asset you want available offline + }); + }, + { once: true } +); +``` + +Any path missing from the map resolves to an empty string, so build the map from the contents of the `assets/` directory rather than by hand — generating it at build time from a directory listing is the practical approach. + +**3. Expect system UI fonts.** Online, the editor injects a Google Fonts stylesheet for Inter and Open Sans. Offline that injection is skipped and the interface falls back to system fonts. Self-host those two families yourself if the fallback matters. + +```jsx + +``` + ## Localization Set `options.locale` (bundled: `en`, `es`, `fr`, `de`, `it`, `pt`, `nl`, `ja`, `ko`, `zh`) and optionally override strings with `options.translations`.