---
title: Svelte
description: The full Files API in the browser, idiomatic Svelte - useFiles returns the verbs plus Svelte stores for ambient state, with useList / useFile / useSearch.
related:
  - /guides/sveltekit-file-upload
---

`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.

```svelte
<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 (a missing one rejects with `Unsupported`) — including the bulk array forms. `search()`'s generator returns a `SearchSummary` (`{ truncated }`) saying whether the gateway's bounded walk stopped early, and a `416` rejects with `Invalid`. They are plain methods:

```ts lineNumbers
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 → { results, errors? }
```

## Ambient state (stores)

```ts lineNumbers
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:

```svelte
<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, keeping its `data` while it reloads. A `signal` in the options cancels the query's request too, which settles it with an `aborted` error.

`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](/docs/ui/server/gateway) (`createFilesRouter` on SvelteKit, Hono, Express, or any Web-`Request` runtime) and lock it down with [`authorize`](/docs/ui/server/authorization).
