Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/uppy-official.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@transloadit/convex": minor
---

- switch the example and docs to Uppy + @uppy/transloadit and remove the React/tus helpers
- add signed assemblyOptions helpers and ensure expected upload counts are included in params
- update docs for the new Uppy-first integration path
43 changes: 26 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Transloadit Convex Component

A Convex component for creating Transloadit Assemblies, handling resumable uploads with tus, and persisting status/results in Convex.
A Convex component for creating Transloadit Assemblies, signing Uppy uploads, and persisting status/results in Convex.

## Features

- Create Assemblies with Templates or inline Steps.
- Resumable uploads via tus (client-side hook; form/XHR uploads are intentionally not supported).
- Signed upload options for Uppy + `@uppy/transloadit`.
- Webhook ingestion with signature verification (direct or queued).
- Persist Assembly status + results in Convex tables.
- Typed API wrappers and React hooks.
- Typed API wrappers and helpers.

## Requirements

Expand Down Expand Up @@ -45,8 +45,8 @@ npx convex env set TRANSLOADIT_SECRET <your_auth_secret>

## Golden path (secure by default)

1. **Server-only create**: a Convex action creates the Assembly (auth secret stays server-side).
2. **Client upload**: use `useTransloaditUppy` for resumable uploads.
1. **Server-only create**: a Convex action creates signed `assemblyOptions` (auth secret stays server-side).
2. **Client upload**: use Uppy + `@uppy/transloadit` with `assemblyOptions()`.
3. **Webhook ingestion**: verify the signature and `queueWebhook` for durable processing.
4. **Realtime UI**: query status/results and render the gallery.

Expand All @@ -59,6 +59,7 @@ import { components } from "./_generated/api";

export const {
createAssembly,
createAssemblyOptions,
handleWebhook,
queueWebhook,
refreshAssembly,
Expand Down Expand Up @@ -129,25 +130,33 @@ const transloadit = new Transloadit(components.transloadit, {
});
```

## React usage (Uppy)
## Uppy client (React example)

```tsx
import { useTransloaditUppy } from "@transloadit/convex/react";
import Uppy from "@uppy/core";
import Transloadit from "@uppy/transloadit";
import { api } from "../convex/_generated/api";

const { startUpload, status, results, stage } = useTransloaditUppy({
uppy,
createAssembly: api.wedding.createWeddingAssembly,
getStatus: api.transloadit.getAssemblyStatus,
listResults: api.transloadit.listResults,
refreshAssembly: api.transloadit.refreshAssembly,
const uppy = new Uppy().use(Transloadit, {
waitForEncoding: true,
assemblyOptions: async () => {
const { assemblyOptions } = await runAction(
api.wedding.createWeddingAssemblyOptions,
{ fileCount, guestName, uploadCode },
);
return assemblyOptions;
},
});

await startUpload({
createAssemblyArgs: { guestName, uploadCode },
});
await uppy.upload();
```
For advanced/legacy helpers (raw parsing, low-level tus uploads, polling utilities), see `docs/advanced.md`.
Note: `assemblyOptions()` is called once per batch, so pass per-file metadata via Uppy file meta
(e.g. `uppy.setFileMeta(fileId, {...})`) and use `fields` for shared values.

Migration note: the `@transloadit/convex/react` entrypoint has been removed; use Uppy +
`@uppy/transloadit` directly.

For status parsing and polling helpers, see `docs/advanced.md`.

## Example app (Next.js + Uppy wedding gallery)

Expand Down
121 changes: 12 additions & 109 deletions docs/advanced.md
Original file line number Diff line number Diff line change
@@ -1,109 +1,6 @@
# Advanced usage

This page collects low-level helpers and optional maintenance tools. These are intentionally
kept out of the main README so new users can follow a single, Uppy-first path.

## Low-level tus helpers (advanced)

If you need a custom uploader (no Uppy), the legacy tus helpers are still available:

```tsx
import {
uploadWithTransloaditTus,
useTransloaditTusUpload,
uploadFilesWithTransloaditTus,
} from "@transloadit/convex/react";
import { api } from "../convex/_generated/api";

function TusUpload() {
const { upload, isUploading, progress } = useTransloaditTusUpload(
api.transloadit.createAssembly,
);

const handleUpload = async (file: File) => {
await upload(file, {
templateId: "template_id_here",
onAssemblyCreated: (assembly) => console.log(assembly.assemblyId),
});
};

return (
<div>
<input type="file" onChange={(e) => handleUpload(e.target.files![0])} />
{isUploading && <p>Uploading: {progress}%</p>}
</div>
);
}
```

Imperative helper (e.g. non-React):

```ts
import { useAction } from "convex/react";

const createAssembly = useAction(api.transloadit.createAssembly);

await uploadWithTransloaditTus(
createAssembly,
file,
{ templateId: "template_id_here" },
{ onStateChange: (state) => console.log(state) },
);
```

Multi-file uploads with concurrency + cancellation:

```ts
import { uploadFilesWithTransloaditTus } from "@transloadit/convex/react";

const controller = uploadFilesWithTransloaditTus(createAssembly, files, {
concurrency: 3,
onFileProgress: (file, progress) => console.log(file.name, progress),
onOverallProgress: (progress) => console.log("overall", progress),
});

// Optional: cancel in-flight uploads
// controller.cancel();

const result = await controller.promise;
console.log(result.files);
```

## Reactive status/results helpers

```tsx
import { useAssemblyStatus, useTransloaditFiles } from "@transloadit/convex/react";
import { api } from "../convex/_generated/api";

function AssemblyStatus({ assemblyId }: { assemblyId: string }) {
const status = useAssemblyStatus(api.transloadit.getAssemblyStatus, assemblyId);
const results = useTransloaditFiles(api.transloadit.listResults, {
assemblyId,
});

if (!status) return null;
return (
<div>
<p>Status: {status.ok}</p>
<p>Results: {results?.length ?? 0}</p>
</div>
);
}
```

Polling fallback (no webhooks):

```tsx
import { useAssemblyStatusWithPolling } from "@transloadit/convex/react";
import { api } from "../convex/_generated/api";

const status = useAssemblyStatusWithPolling(
api.transloadit.getAssemblyStatus,
api.transloadit.refreshAssembly,
assemblyId,
{ pollIntervalMs: 5000, stopOnTerminal: true },
);
```
This page collects optional helpers that build on the Uppy-first integration path.

## Typed helpers (raw payload parsing)

Expand Down Expand Up @@ -145,14 +42,20 @@ type ResizeResult = ResultForRobot<"/image/resize">;
type EncodeResult = ResultForRobot<"/video/encode">;
```

Uppy/Tus wiring:
Polling fallback (no webhooks):

```ts
import { buildTusUploadConfig } from "@transloadit/convex";

const { endpoint, metadata } = buildTusUploadConfig(assembly.data, file, {
fieldName: "file",
import { pollAssembly, isAssemblyTerminal } from "@transloadit/convex";

const controller = pollAssembly({
intervalMs: 5000,
refresh: async () => {
await refreshAssembly({ assemblyId });
},
isTerminal: () => isAssemblyTerminal(status),
});

// controller.stop();
```

## Optional demo template tooling
Expand Down
Loading