React / Vite
Set up UploadKit in a React app with Vite, Create React App, or any React framework.
React/Vite apps use @uploadkitdev/core directly — file routes are defined in the UploadKit dashboard, not in code. For code-defined file routes with TypeScript inference, use the Next.js adapter.
Installation
Install @uploadkitdev/core for the upload client and @uploadkitdev/react for the UI components:
pnpm add @uploadkitdev/core @uploadkitdev/reactnpm install @uploadkitdev/core @uploadkitdev/reactyarn add @uploadkitdev/core @uploadkitdev/reactSet up a backend endpoint
UploadKitProvider communicates with a local backend endpoint that holds your API key server-side. Create a route in your backend framework (Express, Hono, Fastify, etc.) that proxies requests to the UploadKit API:
import { createUploadKitHandler } from '@uploadkitdev/core/server';
// Express example
app.use('/api/uploadkit', createUploadKitHandler({
apiKey: process.env.UPLOADKIT_API_KEY, // server-side only — never exposed to the browser
}));Add your API key to your server's environment — never to any VITE_-prefixed variable (those are bundled into the browser):
UPLOADKIT_API_KEY=uk_live_xxxxxxxxxxxxxxxxxxxxxNever expose your API key in a VITE_-prefixed environment variable. Vite includes VITE_* variables in the browser bundle, making the key visible to anyone who inspects your app. Always keep it on the server.
Add the provider
Wrap your app's root with UploadKitProvider, pointing it at your backend endpoint. Place it inside your router provider if you're using React Router or TanStack Router:
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>,
);The endpoint prop points to your own backend server — not directly to the UploadKit API. Your server holds the API key and proxies upload requests on behalf of the browser.
Use components
Once the provider is in place, you can drop upload components anywhere in your component tree.
UploadButton
import { UploadButton } from '@uploadkitdev/react';
export function AvatarUpload() {
return (
<UploadButton
route="avatarUploader"
onUploadComplete={(files) => {
console.log('Uploaded:', files[0]?.url);
}}
onUploadError={(error) => {
console.error('Upload failed:', error.message);
}}
/>
);
}UploadDropzone
import { UploadDropzone } from '@uploadkitdev/react';
export function DocumentDropzone() {
return (
<UploadDropzone
route="documentUploader"
onUploadComplete={(files) => {
console.log('Documents ready:', files.map((f) => f.url));
}}
/>
);
}Using the headless hook
When you need full control over the upload UI — a custom progress bar, inline file list, or your own drag-and-drop area — use the useUploadKit hook directly:
import { useUploadKit } from '@uploadkitdev/react';
export function CustomUploader() {
const { upload, files, isUploading } = useUploadKit({
route: 'imageUploader',
onUploadComplete: (uploadedFiles) => {
console.log('Done:', uploadedFiles);
},
});
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) upload(file);
};
return (
<div>
<input type="file" accept="image/*" onChange={handleChange} disabled={isUploading} />
{isUploading && <p>Uploading...</p>}
{files.map((f) => (
<div key={f.id}>
<span>{f.name}</span>
<progress value={f.progress} max={100} />
</div>
))}
</div>
);
}Using the core client directly
For maximum control, you can use createProxyClient from @uploadkitdev/core without any React dependency. The proxy client talks to your local backend endpoint (which holds the API key) rather than the UploadKit API directly:
import { createProxyClient } from '@uploadkitdev/core';
const client = createProxyClient({
endpoint: '/api/uploadkit', // your backend endpoint, not the UploadKit API
});
export async function uploadFile(
file: File,
onProgress?: (pct: number) => void,
) {
const result = await client.upload({
file,
route: 'imageUploader',
onProgress,
});
return result; // { id, key, name, size, type, url, status, createdAt }
}This is useful when integrating uploads into form libraries, file managers, or non-React contexts.