Skip to content

Upload Handlers

Mehrdad Pajuhaan edited this page Oct 23, 2026 · 1 revision

Upload Handlers

In edit mode, editors upload images (image elements, backgrounds, gallery slides, layers) and videos (video backgrounds). The Page Builder does not hard-code any upload endpoint. Instead, you give it two functions that return an upload URL for the item being edited. This page explains the contract and shows the setup used with Selldone.

The server option

// Builder.types.ts
type ModelType = "page" | "popup" | "menu";
type IModel = Page | Popup | ShopMenu; // from @selldone/core-js

type IUploadUrlFunction = (type: ModelType, model: IModel) => string | null;

interface IServer {
  uploadImageUrl: IUploadUrlFunction;
  uploadVideoUrl: IUploadUrlFunction;
}

Pass server to SetupPageBuilder or PageHyper in edit mode. Both functions receive:

  • type — what is being edited: "page" (landing page), "popup" or "menu" (a custom page used for menus or notifications),
  • model — the model linked with $builder.setModel(type, model). LPageEditor does this for you from v-model, using isPopup / isMenu to choose the type.

Return the full URL to upload to, or null when uploads are not supported for that type.

Selldone setup

With the Backoffice SDK installed (BackofficeSDK.Setup() creates window.API), use the Selldone dashboard endpoints:

import { SetupPageBuilder } from "@selldone/page-builder";

SetupPageBuilder(app, {
   mode: "edit",
   server: {
      uploadImageUrl: (type, model) => {
         if (type === "page") {
            // Shop landing pages:
            return window.API.POST_UPLOAD_PAGE_IMAGE(model.shop_id, model.id);
         } else if (type === "menu") {
            // Shop custom page for menus:
            return window.API.POST_UPLOAD_PAGE_IMAGE(model.shop_id, "default"); // Save in default (For menus / notifications custom page design)
         } else if (type === "popup") {
            // Shop popups:
            return window.API.POST_UPLOAD_POPUP_IMAGE(model.shop_id, model.id);
         }
         return null;
      },

      uploadVideoUrl: (type, model) => {
         if (type === "page") {
            // Shop landing pages:
            return window.API.POST_UPLOAD_PAGE_VIDEO(model.shop_id, model.id);
         } else if (type === "menu") {
            // Shop custom page for menus:
            return window.API.POST_UPLOAD_PAGE_VIDEO(model.shop_id, "default"); // Save in default (For menus / notifications custom page design)
         } else if (type === "popup") {
            // Shop popups:
            return null;
         }
         return null;
      },
   },
});

The same server object works with PageHyper(app, { mode: "edit", server }).

Validation at setup

SetupPageBuilder calls uploadImageUrl("page", { id: 0, shop_id: 0 }) once during setup to check your function. If it is not a function, or it returns an empty value, an error is logged to the console. Make sure your function returns a string for this dummy model and does not throw.

At upload time, $builder.getImageUploadUrl() and $builder.getVideoUploadUrl() log an error and return nothing when the function is missing or when no model was linked with setModel().

What the upload endpoint receives and returns

The built-in image element (XUploader) sends the file as multipart/form-data in a field named photo with a POST request to the URL you return. It expects a JSON response like:

{
  "files": {
    "path": "https://cdn.example.com/images/abc.jpg",
    "item": { "id": 12, "path": "abc.jpg", "size": 48213 }
  }
}
  • files.path becomes the image src.
  • files.item (optional) is added to the editor asset library set with $builder.setAssets(images, videos).
  • On failure, return { "error": true, "error_msg": "Reason" }; the message is shown to the editor.

Other upload fields in the editor (backgrounds, gallery slides, image layers, typography and dynamic settings) also get their URL from getImageUploadUrl() / getVideoUploadUrl(). Some of them use different upload inputs (for example SImageUploader from Components), so when you build a custom backend, test each of these flows.

Media library

Show previously uploaded files in the editor by passing them to the builder:

$builder.setAssets(
  [{ id: 1, path: "hero.jpg", size: 120034 }],   // images
  [{ id: 7, path: "intro.mp4", size: 5023411 }], // videos (optional)
);

Next steps

Clone this wiki locally