Demos
Replace a Placeholder Image with a User Upload
Mark placeholder images and replace them with user-selected photos in Polotno
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
import React from 'react';
import { reaction } from 'mobx';
export function usePlaceholderSelection(store: any) {
const fileInputRef = React.useRef<HTMLInputElement | null>(null);
// react to selected elements
React.useEffect(() => {
// Dispose reaction when component unmounts
const dispose = reaction(
() => store.selectedElements,
(selectedElements: any[]) => {
// 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: React.ChangeEvent<HTMLInputElement>) => {
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 recommended to upload image to the server first
// for better performance and smaller JSON export
const dataURL = loadEvent.target?.result as string;
// Find the currently selected placeholder element
const placeholder = store.selectedElements.find(
(el: any) => 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 = '';
};
// 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:
import React from 'react';
import { PolotnoContainer, SidePanelWrap, WorkspaceWrap } from 'polotno';
import { Toolbar } from 'polotno/toolbar/toolbar';
import { PagesTimeline } from 'polotno/pages-timeline';
import { ZoomButtons } from 'polotno/toolbar/zoom-buttons';
import { SidePanel } from 'polotno/side-panel';
import { Workspace } from 'polotno/canvas/workspace';
export const App = ({ store }: { store: any }) => {
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.