---
title: UI overview
description: The whole Files API in the browser, over one endpoint — a React hook, a Vue composable, or Svelte stores, backed by a server gateway you mount in minutes.
---

`files-sdk` gives the browser the same API the SDK gives the server. One binding — a [React hook](/docs/ui/client/react), a [Vue composable](/docs/ui/client/vue), or [Svelte stores](/docs/ui/client/svelte) — mirrors every `Files` verb over a single HTTP endpoint:

<Tabs>

<Tab title="React">

```tsx
"use client";
import { useFiles } from "files-sdk/react";

export function Uploader() {
  const files = useFiles({ endpoint: "/api/files" });

  return (
    <>
      <input
        type="file"
        onChange={(e) => {
          const file = e.target.files?.[0];
          if (file) files.upload(file); // keyless — the server mints the key
        }}
      />
      {files.isUploading && <progress value={files.progress.fraction} />}
    </>
  );
}
```

</Tab>

<Tab title="Vue">

```vue
<script setup lang="ts">
import { useFiles } from "files-sdk/vue";

const files = useFiles({ endpoint: "/api/files" });

const onChange = (e: Event) => {
  const file = (e.target as HTMLInputElement).files?.[0];
  if (file) files.upload(file); // keyless — the server mints the key
};
</script>

<template>
  <input type="file" @change="onChange" />
  <progress
    v-if="files.isUploading.value"
    :value="files.progress.value.fraction"
  />
</template>
```

</Tab>

<Tab title="Svelte">

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

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

  const onChange = (e: Event) => {
    const file = (e.currentTarget as HTMLInputElement).files?.[0];
    if (file) files.upload(file); // keyless — the server mints the key
  };
</script>

<input type="file" on:change={onChange} />
{#if $isUploading}<progress value={$progress.fraction} />{/if}
```

</Tab>

</Tabs>

The browser never holds storage credentials. Calls go to **your** endpoint, which runs the SDK against whatever adapter you configured (S3, R2, GCS, Vercel Blob, …) and streams or signs as needed.

## The two halves

|  | Package | What it is |
| --- | --- | --- |
| **Client** | [`files-sdk/react`](/docs/ui/client/react), [`files-sdk/vue`](/docs/ui/client/vue), [`files-sdk/svelte`](/docs/ui/client/svelte) | A `useFiles` binding for your framework — every verb (imperative, with upload progress) plus optional reactive `useList` / `useFile` / `useSearch`. |
| **Server** | `files-sdk/api` + a framework adapter ([Next.js](/docs/ui/server/next), [Hono](/docs/ui/server/hono), [Express](/docs/ui/server/express)) | A [gateway](/docs/ui/server/gateway) you mount at `/api/files` that exposes the `Files` API over HTTP, gated by an [`authorize`](/docs/ui/server/authorization) hook. |

The same gateway backs all three bindings. A framework-agnostic core, `createFilesClient` from `files-sdk/client`, sits under them for non-framework (Node, worker) callers.

:::warning
The gateway proxies `download`, `list`, `delete`, and `move` to the browser — it is effectively a remote storage console. It is **deny-by-default**: nothing is exposed until you configure [`authorize`](/docs/ui/server/authorization) or `operations`. Read that page before shipping.
:::

## Quick start

**1. Mount the gateway.** Expose the `Files` API at an endpoint and scope every key to the signed-in user. This example uses Next.js; [Hono](/docs/ui/server/hono) and [Express](/docs/ui/server/express) are a one-liner too:

```ts title="app/api/files/route.ts" lineNumbers
import { createFiles } from "files-sdk";
import { s3 } from "files-sdk/s3";
import { createFilesRouter } from "files-sdk/api";
import { createRouteHandler } from "files-sdk/next";

const router = createFilesRouter({
  files: createFiles({ adapter: s3({ bucket: "uploads" }) }),
  allowedOrigins: ["https://app.example.com"],
  authorize: async ({ req }) => {
    const session = await auth(req); // throw → 401
    return { keyPrefix: `users/${session.id}/`, maxExpiresIn: 300 };
  },
});

export const { GET, POST, PUT } = createRouteHandler(router);
```

**2. Use your binding.** Drop the component above into your app — that's the whole loop: uploads stream directly to storage (or proxy through your endpoint for adapters that can't presign), and reads run against your gateway. Keys the client sends are relative to the authorized prefix, so the browser can never address another user's files. For a file browser, the reactive reads (`useList` / `useFile` / `useSearch`) wrap the read verbs with `data` / `isLoading` / `refetch`.

Next: pick your binding — [React](/docs/ui/client/react), [Vue](/docs/ui/client/vue), or [Svelte](/docs/ui/client/svelte) — then set up the [gateway](/docs/ui/server/gateway) and its [authorization](/docs/ui/server/authorization).

## Server adapters

The gateway mounts on any of these with a one-line adapter: [Next.js](/docs/ui/server/next), [Hono](/docs/ui/server/hono), [Express](/docs/ui/server/express), [Fastify](/docs/ui/server/fastify), [Koa](/docs/ui/server/koa), [NestJS](/docs/ui/server/nestjs), [Elysia](/docs/ui/server/elysia), [Nitro](/docs/ui/server/nitro), [SvelteKit](/docs/ui/server/sveltekit), [Astro](/docs/ui/server/astro), [TanStack Start](/docs/ui/server/tanstack-start), [Bun](/docs/ui/server/bun), and [Deno](/docs/ui/server/deno).

## Prebuilt components

On top of the React hook there's a [shadcn](https://ui.shadcn.com) registry of SDK-wired components you can `npx shadcn add` and own:

- [File browser](/docs/ui/components/file-browser) - list, navigate, and manage a prefix.
- [File list](/docs/ui/components/file-list) - a lightweight table of stored files.
- [Dropzone](/docs/ui/components/dropzone) - drag-and-drop uploads.
- [Upload progress](/docs/ui/components/upload-progress) - per-file progress UI.
- [Multipart uploader](/docs/ui/components/multipart-uploader) - large-file uploads in parts.
- [File preview](/docs/ui/components/file-preview) - inline previews for stored objects.
- [File search](/docs/ui/components/file-search) - key search over the gateway.
- [File actions](/docs/ui/components/file-actions) - a per-file dropdown of verbs.
- [Share dialog](/docs/ui/components/share-dialog) - mint signed URLs with expiry presets.
- [Capabilities badges](/docs/ui/components/capabilities-badges) - show what the backend supports.
- [Version history](/docs/ui/components/version-history) - browse and restore versions.
- [Trash bin](/docs/ui/components/trash-bin) - soft-deleted files with restore and purge.

Each page has its own install command, or grab all of them at once with the `all` bundle:

<ComponentInstall name="all" />
