Skip to content
FrameworkJavaScript

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.

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
}