What an upload feature actually needs
Letting a user pick an image looks like one <input type="file">. The rest is the part that takes the time: somewhere to put the bytes, a way to get them there from the browser, a limit on what is accepted, and a URL you can show the image from afterwards.
The usual DIY route is an API endpoint that accepts a multipart request, a bucket to store the result, and some code to hand back a URL. That is a fair amount of surface area for a profile picture. A hosted upload service replaces all of it with a component and a key, and that is the route this guide takes.
1. Install the React package
npm install @cd2/fileboy-reactYou also need a public key. Create a free app and copy the key from its Keys page. To try the code first without an account, use the shared key demo. Anything uploaded with it is visible to everyone, so swap in your own key before real use.
2. Wrap your app in the provider
The provider holds your public key. The public key is safe to ship in browser code: it can only do what your app's permission settings allow.
import { FileboyProvider } from "@cd2/fileboy-react"
export function App() {
return (
<FileboyProvider publicKey="YOUR_PUBLIC_KEY">
<ProfileForm />
</FileboyProvider>
)
}3. Drop in the uploader
FileUploader renders a drop zone with a button, shows progress, and calls onFileUploaded when the file has landed. allow works like the accept attribute on a normal file input, so image/* rejects anything that is not an image before it leaves the browser.
import { useState } from "react"
import { FileUploader } from "@cd2/fileboy-react"
export function ProfileForm() {
const [avatarId, setAvatarId] = useState<string>()
return (
<FileUploader
allow="image/*"
onFileUploaded={file => setAvatarId(file.id)}
/>
)
}The callback receives the file's data: id, name, mime, fileSize, hash and createdAt, plus any metadata you attached. Save the `id`. That one string is all your database needs.
4. Show the image
Image takes the ID and builds the URL for you. Because images are resized by URL, you can ask for exactly the size you are displaying instead of shipping the full upload.
import { useState } from "react"
import { FileboyProvider, FileUploader, Image } from "@cd2/fileboy-react"
function ProfileForm() {
const [avatarId, setAvatarId] = useState<string>()
return (
<div>
<FileUploader
allow="image/*"
onFileUploaded={file => setAvatarId(file.id)}
/>
{avatarId && (
<Image
id={avatarId}
transforms={[{ type: "resize", width: 160, height: 160, fit: "cover" }]}
/>
)}
</div>
)
}
export function App() {
return (
<FileboyProvider publicKey="YOUR_PUBLIC_KEY">
<ProfileForm />
</FileboyProvider>
)
}Store the file ID, not the URL. The URL changes with the size and format you ask for. The ID does not, so one stored value can serve a thumbnail today and a full-width banner next month.
Things to decide before you ship
- Who may upload. The public key is meant to be in browser code, so anyone who has it can upload to your app. Limit file types with
allowfor your users' benefit, and keep an eye on usage in the dashboard. - More than one file. Swap
FileUploaderforMultiFileUploaderand useonFilesUploaded, which receives an array. - Not using React. The same uploader is available as a script tag. See the JavaScript library docs.
- Existing files. Pass
defaultFileIdto show a previously uploaded file in the uploader when editing a record.
Checked against the current libraries in November 2026. Found something that no longer works? Tell us.