Complete Example

This page walks through the key concepts behind the example Unsplash plugin — a reference implementation of a custom source with a search UI, activity header, and file picking.

Activity UI

The render function receives a host element — an empty container inside the uploader’s DOM. You build your activity UI by appending children to it. The uploader provides a set of CSS classes and built-in elements that automatically inherit the current theme.

You are not limited to web components or vanilla DOM. The host element is a regular DOM node, so you can mount a React component with createRoot(host).render(...), a Vue app with createApp(...).mount(host), a Svelte component, or anything else that can render into a DOM container. The example Unsplash plugin uses Lit for convenience, but any UI framework or library works.

Activity header

Activities typically include a header bar for navigation. Use the .uc-ui-activity-header class on a div with three direct children:

  1. Back button — an .uc-ui-icon-btn that calls uploaderApi.historyBack() to return to the previous screen.
  2. Center slot — a div containing an icon and a title.
  3. Close button — an .uc-ui-icon-btn that calls uploaderApi.setModalState(false) to close the modal.
1<div class="uc-ui-activity-header">
2 <button type="button" class="uc-ui-icon-btn" title="Back" aria-label="Back">
3 <uc-icon name="back"></uc-icon>
4 </button>
5 <div>
6 <uc-icon name="unsplash"></uc-icon>
7 <span>Unsplash</span>
8 </div>
9 <button type="button" class="uc-ui-icon-btn" title="Close" aria-label="Close">
10 <uc-icon name="close"></uc-icon>
11 </button>
12</div>

Use the built-in <uc-icon> element for icons. Built-in names like back and close are always available; plugin-registered icons (via registerIcon) are also accessible by name. Since <uc-icon> is a standard Web Component, it works in any framework — Lit templates, React JSX, Vue templates, etc.

Adding files to the upload list

When a user picks a photo, call uploaderApi.addFileFromUrl() to add it to the upload list. The uploader handles downloading, uploading, and progress tracking automatically. After adding the file, navigate the user to the upload list so they can see progress:

1uploaderApi.addFileFromUrl(photo.urls.full, {
2 fileName: `unsplash-${photo.id}.jpg`,
3});
4uploaderApi.setCurrentActivity('upload-list');
5uploaderApi.setModalState(true);

Other methods are available depending on what you have:

Using a framework

The render function’s host is a plain DOM node. You can mount any framework into it and clean up in the returned function. For example, with React:

1pluginApi.registry.registerActivity({
2 id: UNSPLASH_ACTIVITY_ID,
3 render(host) {
4 const root = createRoot(host);
5
6 // Render immediately with the current value, then re-render on every change
7 const unsubscribe = pluginApi.config.subscribe('unsplashAccessKey', (accessKey) => {
8 root.render(
9 <UnsplashGallery uploaderApi={uploaderApi} accessKey={accessKey} />,
10 );
11 });
12
13 return () => {
14 unsubscribe();
15 root.unmount();
16 };
17 },
18});

config.subscribe fires immediately with the current value and again on every change, so wrapping root.render inside it keeps the React tree in sync with config updates.

The same pattern applies to Vue (createApp(...).mount(host)), Svelte, Solid, or any other library that can render into a DOM container.

Usage

To expose this source in the picker, add unsplash to sourceList and pass the plugin:

1<uc-config
2 ctx-name="my-uploader"
3 pubkey="YOUR_PUBLIC_KEY"
4 source-list="local, unsplash"
5 unsplash-access-key="YOUR_UNSPLASH_KEY"
6></uc-config>
1document.querySelector('uc-config').plugins = [unsplashPlugin];

See the example Unsplash plugin source for the complete implementation.