# Custom Asset Picker

> Let your users pick images, videos, fonts, and colors from your own library

- **URL**: https://orshot.com/docs/orshot-embed/custom-asset-picker

---

The Custom Asset Picker lets you replace the default file upload dialogs with your own asset selection UI. When a user needs to insert an image, video, font, or color, the embed sends an event to your parent page — you show your own picker, and send the selected asset back.

This is useful when you already have an asset library (DAM, media gallery, brand kit) and want your users to pick from it instead of uploading files manually.

## How It Works

1. User clicks "Insert Image" (or video, font, color) in the embed
2. The embed sends an `orshot:asset:request` event to your parent page
3. Your app opens its own picker UI
4. User selects an asset in your UI
5. Your app sends an `orshot:asset:response` message back to the embed
6. The embed downloads the asset from the URL you provide and saves it to the user's library

## Enabling Custom Asset Picker

1. Go to your embed settings
2. Under **Custom Asset Picker**, enable the asset types you want to handle (image, video, font, color)
3. Save your settings

You can enable the picker for specific asset types independently — for example, handle only images and colors from your library while letting users upload fonts normally.

## Handling Asset Requests

Listen for `orshot:asset:request` events from the embed:```javascript
window.addEventListener("message", (event) => {
  if (!event.origin.includes("orshot.com")) return;

  if (event.data.type === "orshot:asset:request") {
    const { type, requestId } = event.data.data;

    // type: "image", "video", "font", or "color"
    // requestId: unique ID to match your response

    openYourAssetPicker(type, requestId);
  }
});
```## Sending Asset Responses

Once the user picks an asset, send it back to the embed. The response format varies by asset type.

### Image```javascript
const iframe = document.querySelector("iframe");

iframe.contentWindow.postMessage(
  {
    type: "orshot:asset:response",
    requestId: "the-request-id",
    url: "https://your-cdn.com/images/photo.jpg",
    fileName: "photo.jpg", // optional
  },
  "*"
);
```### Video```javascript
iframe.contentWindow.postMessage(
  {
    type: "orshot:asset:response",
    requestId: "the-request-id",
    url: "https://your-cdn.com/videos/clip.mp4",
    fileName: "clip.mp4", // optional
  },
  "*"
);
```### Font```javascript
iframe.contentWindow.postMessage(
  {
    type: "orshot:asset:response",
    requestId: "the-request-id",
    url: "https://your-cdn.com/fonts/CustomFont.woff2",
    fontFamily: "Custom Font", // required — the display name
  },
  "*"
);
```### Color```javascript
iframe.contentWindow.postMessage(
  {
    type: "orshot:asset:response",
    requestId: "the-request-id",
    value: "#FF6B00", // hex color value
  },
  "*"
);
```## What Happens After You Respond

The embed takes care of everything after you send the response:

- **Images/Videos**: Downloads the file from your URL, uploads it to the user's workspace storage, and adds it to their asset library
- **Fonts**: Downloads the font file, uploads it to storage, registers it so it's available in the font picker
- **Colors**: Saves the hex value to the user's saved colors

The asset is permanently available in the user's library for future use — they don't need to re-pick it.

## Complete Example```javascript
const iframe = document.querySelector("iframe");

window.addEventListener("message", (event) => {
  if (!event.origin.includes("orshot.com")) return;

  if (event.data.type === "orshot:asset:request") {
    const { type, requestId } = event.data.data;

    // Open your custom picker based on asset type
    switch (type) {
      case "image":
        showImagePicker((selectedImage) => {
          iframe.contentWindow.postMessage(
            {
              type: "orshot:asset:response",
              requestId,
              url: selectedImage.url,
              fileName: selectedImage.name,
            },
            "*"
          );
        });
        break;

      case "font":
        showFontPicker((selectedFont) => {
          iframe.contentWindow.postMessage(
            {
              type: "orshot:asset:response",
              requestId,
              url: selectedFont.fileUrl,
              fontFamily: selectedFont.name,
            },
            "*"
          );
        });
        break;

      case "color":
        showColorPicker((hex) => {
          iframe.contentWindow.postMessage(
            {
              type: "orshot:asset:response",
              requestId,
              value: hex,
            },
            "*"
          );
        });
        break;
    }
  }
});
```## React Example```jsx
import { useEffect, useRef, useState } from "react";

function EmbedWithAssetPicker() {
  const iframeRef = useRef(null);
  const [pickerState, setPickerState] = useState(null);

  useEffect(() => {
    const handleMessage = (event) => {
      if (!event.origin.includes("orshot.com")) return;

      if (event.data.type === "orshot:asset:request") {
        const { type: assetType, requestId } = event.data.data;
        setPickerState({ assetType, requestId });
      }
    };

    window.addEventListener("message", handleMessage);
    return () => window.removeEventListener("message", handleMessage);
  }, []);

  const handleAssetSelected = (asset) => {
    if (!pickerState) return;

    const response = {
      type: "orshot:asset:response",
      requestId: pickerState.requestId,
    };

    if (pickerState.assetType === "color") {
      response.value = asset.hex;
    } else if (pickerState.assetType === "font") {
      response.url = asset.url;
      response.fontFamily = asset.name;
    } else {
      response.url = asset.url;
      response.fileName = asset.name;
    }

    iframeRef.current?.contentWindow?.postMessage(response, "*");
    setPickerState(null);
  };

  return (
    <div>
      <iframe
        ref={iframeRef}
        src="https://orshot.com/embeds/YOUR_EMBED_ID"
        style={{ width: "100%", height: "700px" }}
      />

      {pickerState && (
        <YourAssetPickerModal
          type={pickerState.assetType}
          onSelect={handleAssetSelected}
          onClose={() => setPickerState(null)}
        />
      )}
    </div>
  );
}
```## Asset URL Requirements

- URLs must be publicly accessible (the embed server downloads the file)
- HTTPS is required for production
- Private/internal URLs (localhost, 10.x.x.x, 192.168.x.x) are blocked
- Maximum file size: 10 MB
- Supported formats:
  - **Images**: PNG, JPEG, GIF, WebP, SVG, AVIF
  - **Videos**: MP4, WebM, MOV
  - **Fonts**: WOFF, WOFF2, TTF, OTF

## Event Reference

| Event | Direction | Description |
| --- | --- | --- |
| `orshot:asset:request` | Embed → Parent | User wants to pick an asset |
| `orshot:asset:response` | Parent → Embed | Your app sends the selected asset |

### Request Payload```javascript
{
  type: "orshot:asset:request",
  timestamp: "2024-01-15T10:30:00.000Z",
  data: {
    type: "image", // "image", "video", "font", or "color"
    requestId: "req-abc123"
  }
}
```### Response Payload (Image/Video)```javascript
{
  type: "orshot:asset:response",
  requestId: "req-abc123",
  url: "https://...",
  fileName: "optional-name.jpg"
}
```### Response Payload (Font)```javascript
{
  type: "orshot:asset:response",
  requestId: "req-abc123",
  url: "https://...",
  fontFamily: "Font Display Name"  // required
}
```### Response Payload (Color)```javascript
{
  type: "orshot:asset:response",
  requestId: "req-abc123",
  value: "#FF6B00"
}
```