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
46 changes: 42 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
Store and serve files with Cloudflare R2.

```ts
// or @convex-dev/r2/svelte for Svelte!
import { useUploadFile } from "@convex-dev/r2/react";

// Upload files from React
const uploadFile = useUploadFile(api.example);
// ...in a callback
Expand Down Expand Up @@ -90,8 +93,7 @@ npx convex env set R2_BUCKET xxxxx

## Uploading files

File uploads to R2 typically use signed urls. The R2 component provides a React
hook that handles the entire upload processs:
File uploads to R2 typically use signed urls. The R2 component provides hooks for React and Svelte that handle the entire upload process:

- generates the signed url
- uploads the file to R2
Expand Down Expand Up @@ -121,7 +123,9 @@ hook that handles the entire upload processs:
});
```

2. Use the `useUploadFile` hook in a React component to upload files:
2. Use the `useUploadFile` hook in your component to upload files:

React:

```tsx
// src/App.tsx
Expand Down Expand Up @@ -165,6 +169,40 @@ hook that handles the entire upload processs:
}
```

Svelte:

```svelte
<script lang="ts">
import { useUploadFile } from "@convex-dev/r2/svelte";
import { api } from "../convex/_generated/api";

const uploadFile = useUploadFile(api.example);

let selectedImage = $state<File | null>(null);

async function handleUpload(file: File) {
await uploadFile(file);
selectedImage = null;
}
</script>

<form
onsubmit={() => {
if (selectedImage) handleUpload(selectedImage);
}}
>
<input
type="file"
accept="image/*"
onchange={(e) => {
selectedImage = e.currentTarget.files?.[0] ?? null;
}}
disabled={selectedImage !== null}
/>
<button type="submit" disabled={selectedImage === null}> Upload </button>
</form>
```

### Using a custom object key

The `r2.generateUploadUrl` function generates a uuid to use as the object key by
Expand Down Expand Up @@ -379,7 +417,7 @@ export const page = query({
### Accessing metadata after upload

The `onSyncMetadata` callback can be used to run a mutation after every metadata
sync. The `useUploadFile` React hook syncs metadata after every upload, so this
sync. The `useUploadFile` hook syncs metadata after every upload, so this
function will run each time as well.

Because this runs after metadata sync, the `r2.getMetadata` can be used to
Expand Down
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@
"default": "./dist/commonjs/react/index.js"
}
},
"./svelte": {
"import": {
"@convex-dev/component-source": "./src/svelte/index.ts",
"types": "./dist/esm/svelte/index.d.ts",
"default": "./dist/esm/svelte/index.js"
},
"require": {
"@convex-dev/component-source": "./src/svelte/index.ts",
"types": "./dist/commonjs/svelte/index.d.ts",
"default": "./dist/commonjs/svelte/index.js"
}
},
"./convex.config": {
"import": {
"@convex-dev/component-source": "./src/component/convex.config.ts",
Expand All @@ -68,6 +80,8 @@
},
"peerDependencies": {
"convex": "~1.16.5 || >=1.17.0 <1.35.0",
"convex-svelte": "^0.0.11",
"svelte": ">=5.0.0",
"react": "^18 || ^19",
"react-dom": "^18 || ^19"
},
Expand Down
Loading