Custom Asset Picker

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


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:

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#

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#

iframe.contentWindow.postMessage(
  {
    type: "orshot:asset:response",
    requestId: "the-request-id",
    url: "https://your-cdn.com/videos/clip.mp4",
    fileName: "clip.mp4", // optional
  },
  "*"
);

Font#

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#

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#

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#

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#

EventDirectionDescription
orshot:asset:requestEmbed → ParentUser wants to pick an asset
orshot:asset:responseParent → EmbedYour app sends the selected asset

Request Payload#

{
  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)#

{
  type: "orshot:asset:response",
  requestId: "req-abc123",
  url: "https://...",
  fileName: "optional-name.jpg"
}

Response Payload (Font)#

{
  type: "orshot:asset:response",
  requestId: "req-abc123",
  url: "https://...",
  fontFamily: "Font Display Name"  // required
}

Response Payload (Color)#

{
  type: "orshot:asset:response",
  requestId: "req-abc123",
  value: "#FF6B00"
}

Ready to automate?

Start rendering images, PDFs and videos from your templates in under 2 minutes. Free plan, no credit card.

Get your API key
  • Image, PDF and video generation via API
  • Visual editor with AI and smart layouts
  • Zapier, Make, MCP and 50+ integrations
  • White-label embed for your own app
  • 60 free renders — no credit card required