> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://uploadcare.com/docs/llms.txt.

# Complete Example

> Full walkthrough of an Unsplash source plugin.

This page walks through the key concepts behind the [example Unsplash plugin](https://github.com/uploadcare/blocks/blob/main/src/plugins/unsplashPlugin.ts) — 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](/docs/file-uploader/plugins/activity-ui/) and built-in elements that automatically inherit the current theme.

> **Tip**
>
> **Tip:** 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](https://lit.dev/) 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()`](/docs/file-uploader/api/#history-back) 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.

```html
<div class="uc-ui-activity-header">
  <button type="button" class="uc-ui-icon-btn" title="Back" aria-label="Back">
    <uc-icon name="back"></uc-icon>
  </button>
  <div>
    <uc-icon name="unsplash"></uc-icon>
    <span>Unsplash</span>
  </div>
  <button type="button" class="uc-ui-icon-btn" title="Close" aria-label="Close">
    <uc-icon name="close"></uc-icon>
  </button>
</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`](/docs/file-uploader/plugins/assets/#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()`](/docs/file-uploader/api/#add-file-from-url) 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:

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

Other methods are available depending on what you have:

* [`addFileFromObject()`](/docs/file-uploader/api/#add-file-from-object): for `File` or `Blob` instances.
* [`addFileFromUuid()`](/docs/file-uploader/api/#add-file-from-uuid): for files already uploaded to Uploadcare.
* [`addFileFromCdnUrl()`](/docs/file-uploader/api/#add-file-from-cdn-url): for existing Uploadcare CDN URLs.

### 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:

```tsx
pluginApi.registry.registerActivity({
  id: UNSPLASH_ACTIVITY_ID,
  render(host) {
    const root = createRoot(host);

    // Render immediately with the current value, then re-render on every change
    const unsubscribe = pluginApi.config.subscribe('unsplashAccessKey', (accessKey) => {
      root.render(
        <UnsplashGallery uploaderApi={uploaderApi} accessKey={accessKey} />,
      );
    });

    return () => {
      unsubscribe();
      root.unmount();
    };
  },
});
```

`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`](/docs/file-uploader/options/#source-list) and pass the plugin:

```html
<uc-config
  ctx-name="my-uploader"
  pubkey="YOUR_PUBLIC_KEY"
  source-list="local, unsplash"
  unsplash-access-key="YOUR_UNSPLASH_KEY"
></uc-config>
```

```javascript
document.querySelector('uc-config').plugins = [unsplashPlugin];
```

See the [example Unsplash plugin source](https://github.com/uploadcare/blocks/blob/main/src/plugins/unsplashPlugin.ts) for the complete implementation.