---
title: "Blok Uploader API — upload assets by kind"
description: "Route file and URL uploads by asset kind, so an audio block’s cover art reaches your image pipeline instead of your audio endpoint."
source: https://blokeditor.com/docs/uploader-api/
lastmod: 2026-09-08
---

Framework JavaScript

Extending & system Uploader

On this page uploader.uploadByFile(file, ctx)

# Uploader API: upload assets by kind

Upload an asset through the pipeline that owns its KIND, instead of whichever tool happens to be asking. Tools call this rather than reaching into their own `config.uploader`. That is why an audio block's cover art reaches your image pipeline, instead of the audio endpoint that would reject it.

Resolution order for a kind: first the tool whose static `assetKind` matches (for example `tools.image.config.uploader` for `'image'`), then the editor-level `uploader` config, then a local fallback. The fallback is a `blob:` URL for files, and the URL verbatim for links.

See the storage presets page for ready-made `uploader` implementations: Supabase, S3-compatible storage, Cloudinary, and IndexedDB. None of them need a backend of your own.

Last updated Sep 8, 2026 [Edit this page on GitHub](https://github.com/JackUait/blok/blob/main/docs/src/components/api/api-data.ts)

### Reaching the editor instance

The methods below run on the editor you created with new Blok(). They are available once editor.isReady resolves.

TypeScript

```
// You already hold the instance returned by the constructor.
const editor = new Blok({ holder: 'editor' });
await editor.isReady;

// Call any API method on it.
editor.caret.setToLastBlock('end');
```

## Methods

### uploader.uploadByFile(file, ctx)

Promise<{ url: string; fileName?: string }>

Store a file and return its URL. `ctx` is `{ kind, tool?, onProgress? }`, where `kind` is the ASSET kind, not the requesting tool. The two differ whenever a tool holds an asset outside its own media family.

TypeScript

```
// Inside a custom block tool that holds a thumbnail
const { url } = await this.api.uploader.uploadByFile(file, {
  kind: 'image',
  tool: 'my-card',
  onProgress: (percent) => this.showProgress(percent),
});
```

### uploader.uploadByUrl(url, ctx)

Promise<{ url: string; fileName?: string }>

Re-host an asset the user supplied by URL, and return the stored URL. Without an uploader for the kind, the URL is stored verbatim. You should configure one if a strict `img-src`/`media-src` policy or link rot would break third-party URLs.

TypeScript

```
const { url } = await this.api.uploader.uploadByUrl(pastedUrl, {
  kind: 'image',
  tool: 'my-card',
});
```

### uploader.isConfigured(kind, method?)

boolean

Whether a host uploader handles this kind. False means the caller would get the local fallback. That helps you decide whether an asset is worth uploading at all, for example inlining small extracted artwork as a `data:` URL instead.

TypeScript

```
if (this.api.uploader.isConfigured('image', 'uploadByFile')) {
  const { url } = await this.api.uploader.uploadByFile(artwork, { kind: 'image' });
} else {
  // no image pipeline — keep it inline rather than minting a doomed blob: URL
}
```
