Skip to content
Files SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

Svelte

The full Files API in the browser, idiomatic Svelte - useFiles returns the verbs plus Svelte stores for ambient state, with useList / useFile / useSearch.

files-sdk/svelte brings the full Files API to the browser as idiomatic Svelte. useFiles returns one method per Files verb — upload, download, url, list, and the rest — plus ambient upload/error state as Svelte stores you read with the $ prefix.

<script lang="ts">
  import { useFiles } from "files-sdk/svelte";
  import { onDestroy } from "svelte";

  const files = useFiles({ endpoint: "/api/files" });
  const { isUploading, progress, error } = files;
  onDestroy(files.abort); // cancel any in-flight calls on unmount

  async function onUpload(event: Event) {
    const file = (event.currentTarget as HTMLInputElement).files?.[0];
    if (file) {
      await files.upload(file);
    }
  }
</script>

<input type="file" on:change={onUpload} />
{#if $isUploading}<progress value={$progress.fraction} />{/if}
{#if $error}<p>{$error.message}</p>{/if}

The binding ships no Svelte runtime — its stores are a tiny implementation of the store contract, so $store auto-subscription works exactly as with writable.

The verbs

Every verb mirrors the SDK — upload, download, head, exists, list, listAll, search, url, delete, copy, move, signedUploadUrl, capabilities, plus the plugin verbs (versions, restoreVersion, trashed, restoreTrashed, purge) when the gateway exposes them — including the bulk array forms. They are plain methods:

const { key } = await files.upload(file); // keyless → server mints the key
const stored = await files.download("report.pdf"); // → a lazy StoredFile
const link = await files.url("avatar.png"); // → string, for <img src>
await files.delete(["a.txt", "b.txt"]); // bulk → { deleted, errors? }

Ambient state (stores)

files.isUploading; // Readable<boolean> → $isUploading
files.uploads; // Readable<FileUploadState[]> - one entry per file, across every upload() call
files.progress; // Readable<{ loaded, total, fraction }> over the current entries
files.error; // Readable<FilesError | undefined> - last error from any verb
files.reset(); // clear the error + finished uploads (re-arms after abort)
files.abort(); // abort every in-flight call - wire to onDestroy

uploads accumulates one entry per file from every upload() call, including each item of a bulk upload([...]). Each entry keeps its position and is replaced by a fresh snapshot on every change. Its status ends as "success", "error" (with error set), or "aborted" on every path. reset() leaves uploads that are still running in place. error also catches failures thrown while iterating listAll()/search().

The binding imports no Svelte lifecycle hooks, so cancel useFiles work by calling files.abort from onDestroy, as above.

Reactive reads

The query stores load on creation and expose data / error / isLoading / isFetching stores plus refetch(). A query reads its options once, so to follow a changing input, re-create it in a $: block:

<script lang="ts">
  import { useList } from "files-sdk/svelte";

  export let prefix = "docs/";

  // A new query whenever `prefix` changes.
  $: ({ data, isLoading } = useList({ prefix }));
</script>

{#if $isLoading}Loading…{:else}
  <ul>{#each $data?.items ?? [] as item}<li>{item.key}</li>{/each}</ul>
{/if}

When the last subscriber of a query’s stores goes away, its in-flight request is aborted, like React and Vue do on unmount. That happens when the component is destroyed, or when a $: block swaps in a new query. A one-off read such as get(store) doesn’t count. refetch() re-runs the same query on demand.

useFile(key) (a head() for previews) and useSearch(pattern, opts) work the same way.

Setting up the gateway

Point the binding at a mounted gateway (createFilesRouter on SvelteKit, Hono, Express, or any Web-Request runtime) and lock it down with authorize.

Was this page helpful?