Replacing a Placeholder Image with a User-Uploaded Photo

Use this approach when you want to give users the ability to quickly replace a default placeholder image in their design with a custom photo. This is useful for flyers, posters, and other templates where large images can be swapped out.

1. Mark an Image as a Placeholder

const width = 600;
const height = 400;
// Add a placeholder image element
// you can make you own with your own text
// placehold.co is just an example, better to use your own image
store.activePage.addElement(
  {
    type: 'image',
    src: 'https://placehold.co/600x400?text=Click+to+add+image',
    x: (store.width - width) / 2,
    y: (store.height - height) / 2,
    width,
    height,
    // custom property to identify placeholder
    custom: {
      isPlaceholder: true,
    },
  },
  { skipSelect: true }
);

2. Detect When the Placeholder Is Selected

Watch for changes in store.selectedElements. If a placeholder image is selected, show an “Upload Your Picture” button or open a file picker immediately.

3. Replace the Placeholder Image

Once a user selects an image file, set the element’s src to the url of that file and clear out custom.isPlaceholder.

Implementation Example

Below is a minimal React-based implementation. The key points are:

  • A hidden file input used to pick images.

  • A MobX reaction or state check to detect when the user selects the placeholder.

  • Replacing the placeholder image with the uploaded file.

export function usePlaceholderSelection(store) {
  const fileInputRef = React.useRef(null);

  // react to selected elements
  React.useEffect(() => {
    // Dispose reaction when component unmounts
    const dispose = reaction(
      () => store.selectedElements,
      (selectedElements) => {
        // Check if at least one selected element is a placeholder
        const placeholder = selectedElements.find(
          (el) => el.custom?.isPlaceholder
        );
        if (placeholder) {
          // Trigger the hidden file input
          fileInputRef.current?.click();
        }
      }
    );
    return () => dispose();
  }, [store]);

  // handle file change
  const handleFileChange = (e) => {
    const file = e.target.files?.[0];
    if (!file) {
      return;
    }
    const reader = new FileReader();
    reader.onload = (loadEvent) => {
      // we will use dataURL to set the image
      // but it is recommented to upload image to the server first
      // for better performance and smaller JSON export
      const dataURL = loadEvent.target.result;
      // Find the currently selected placeholder element
      const placeholder = store.selectedElements.find(
        (el) => el.custom?.isPlaceholder
      );
      if (placeholder) {
        placeholder.set({
          src: dataURL,
          custom: { isPlaceholder: false },
        });
      }
    };
    reader.readAsDataURL(file);
    // Reset the file input so selecting the same file triggers onChange again
    e.target.value = null;
  };

  // Return a hidden file input to be triggered by the reaction
  return (
    <input
      ref={fileInputRef}
      type="file"
      style={{ display: 'none' }}
      accept="image/*"
      onChange={handleFileChange}
    />
  );
}

Then we can use such hook in the app:

export const App = ({ store }) => {
  const fileInput = usePlaceholderSelection(store);

  return (
    <PolotnoContainer style={{ width: '100vw', height: '100vh' }}>
      <SidePanelWrap>
        <SidePanel store={store} />
      </SidePanelWrap>
      <WorkspaceWrap>
        <Toolbar store={store} downloadButtonEnabled />
        <Workspace store={store} />
        {fileInput}
        <ZoomButtons store={store} />
        <PagesTimeline store={store} />
      </WorkspaceWrap>
    </PolotnoContainer>
  );
};

Alternative UI Approaches

  • Manual Button: Instead of automatically triggering the file input, you can display a button in the Toolbar or Tooltip. Clicking it would open the same file dialog.

  • Side Panel Section: Create a custom side panel tab that appears only when a placeholder is selected, displaying an “Upload Image” button inside the panel.

Tips

  • Use a custom property (custom.isPlaceholder) or any property you prefer to mark which images can be replaced.

  • Provide instructions or hints to the end user so they know where and how to upload.

  • When the user’s image is loaded, consider adjusting its bounding box or aspect ratio if needed.

News, updates and promos – be the first to get 'em

News, updates and promos – be the first to get 'em