A React file upload library for Vite
Drop UploadKit components into a Vite + React app and point them at a small backend that signs uploads. The React package gives you the dropzone, button and headless hook. Your server holds the API key, because a Vite SPA cannot keep a secret.
Set up uploads in a Vite + React app
This follows the React / Vite guide in the docs. You need two pieces: the provider in the SPA and an endpoint on a server.
1. Install
To start from a project, run npx create-uploadkit-app my-app --template vite. It is a pure SPA with no server code, so you still add the backend below.
npx create-uploadkit-app my-app --template vitepnpm add @uploadkitdev/core @uploadkitdev/react2. Add a backend endpoint
The docs show an Express handler. UploadKit also ships Hono, Fastify and Express adapters inside @uploadkitdev/next, so a Vite project can reuse the same file router on any of them. That package lists next as a peer dependency, so your package manager may print a warning on a server that does not use Next.js.
pnpm add @uploadkitdev/next honoimport { Hono } from 'hono';
import { createHonoHandler } from '@uploadkitdev/next/hono';
import { ourFileRouter } from './file-router';
const app = new Hono();
app.all(
'/api/uploadkit/*',
createHonoHandler({
router: ourFileRouter,
apiKey: process.env.UPLOADKIT_API_KEY,
})
);Keep UPLOADKIT_API_KEY in the server environment. Never put it in a VITE_ variable, because Vite inlines those into the browser bundle.
3. Wrap the app with the provider
import React from 'react';
import ReactDOM from 'react-dom/client';
import { UploadKitProvider } from '@uploadkitdev/react';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<UploadKitProvider endpoint="/api/uploadkit">
<App />
</UploadKitProvider>
</React.StrictMode>,
);4. Use a component
import { UploadDropzone } from '@uploadkitdev/react';
export function DocumentDropzone() {
return (
<UploadDropzone
route="documentUploader"
onUploadComplete={(files) => {
console.log('Documents ready:', files.map((f) => f.url));
}}
/>
);
}What UploadKit gives a Vite app
The browser side is React. The signing side is your server.
React upload components
UploadButton, UploadDropzone and the rest of the more than 40 components, plus a headless useUploadKit hook for a custom UI.
Presigned direct-to-storage uploads
Your endpoint authorises the request and the browser uploads straight to storage.
Bring your own storage
Run the handler yourself with a storage config for S3, R2 or MinIO, or sign presigned URLs from any backend that follows the template contract.
File routes defined on the server
The handler takes a file router with size limits, allowed types and callbacks. The docs note that React/Vite apps built on the core client define file routes in the dashboard, and that code-defined routes with TypeScript inference come from the Next.js adapter.
Build your own UI with the hook
Use useUploadKit when you want your own progress bar or file list instead of a prebuilt component.
import { useUploadKit } from '@uploadkitdev/react';
export function CustomUploader() {
const { upload, progress, isUploading, result, error } = useUploadKit('imageUploader');
const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
await upload(file);
}
};
return (
<div>
<input type="file" accept="image/*" onChange={handleChange} disabled={isUploading} />
{isUploading && <progress value={progress} max={100} />}
{result && <p>Uploaded: <a href={result.url}>{result.name}</a></p>}
{error && <p>Error: {error.message}</p>}
</div>
);
}For the trade-offs of signing URLs on a server, read presigned URLs vs server proxy uploads. The Remix and React Router tutorial shows the same action-plus-client pattern in a framework that has a built-in server, and the component gallery lists what you can render.
Free
5 GB storage, 2 GB bandwidth per month, 4 MB max file, 1,000 uploads per month, 2 projects.
Pro — $15/mo
100 GB storage, 200 GB bandwidth, 512 MB max file, 50,000 uploads per month, 10 projects.
Team — $35/mo
1 TB storage, 2 TB bandwidth, 5 GB max file, 500,000 uploads per month, 50 projects.
Enterprise
Custom limits. See the pricing page.
Frequently asked questions
Can a Vite app upload files without a backend?
No. A Vite + React app is a client-only SPA and cannot sign upload requests on its own. The CLI prints a warning saying a server is needed to sign requests, either your own backend or BYOS presigned URLs. Your API key must stay on that server.
Which backend should I use?
Any server that exposes the UploadKit endpoint. The docs show Express as the main example, and @uploadkitdev/next ships adapters for Express, Fastify and Hono that reuse the same file router. The starter template also documents a presigned-URL contract (POST filename and contentType, receive url, key and method) for backends of your choice. Backend adapters
Is it safe to put the API key in a VITE_ variable?
No. Vite includes every VITE_ variable in the browser bundle, so anyone can read it. Keep the UploadKit API key on your server. The starter template says to use at most a uk_test key in client-exposed variables and never a uk_live key.
What does npx uploadkit init change in a Vite project?
It detects vite plus react, wraps the contents of <StrictMode> in src/main.tsx with UploadKitProvider endpoint="/api/uploadkit", adds an env entry and installs @uploadkitdev/react. It requires a <StrictMode> element in main.tsx and stops with an error if it is missing. It creates no server code.
Does it work with React Router or TanStack Router?
Yes. Place UploadKitProvider inside your router provider. Documentation for React and Vite says the provider works with React Router or TanStack Router. React setup docs
What does it cost?
The free plan includes 5 GB storage, 2 GB bandwidth per month, a 4 MB maximum file size and 1,000 uploads per month. Pro is $15 per month and Team is $35 per month. See pricing
Add uploads to your Vite app
Start on the free plan: 5 GB storage, 2 GB bandwidth per month, no credit card.