Client intake often requires two types of information: searchable answers and files for review. This Vite and React example collects both in the same response.
You can try the form without an account.
Ask only what you need
The example asks for:
- the client's name;
- a work email address;
- the result they need;
- an optional target date;
- up to five briefs or reference files.
Each answer should help someone prepare for the first call. Leave detailed discovery questions for the call.
Define the form
The form schema lives in the React app:
import { createClient, defineForm, FilloForm } from "@usefillo/react";
const intake = defineForm({
id: "vite-client-intake",
title: "Tell us about your project",
description: "Tell us what you need, when you need it and which files will help us prepare.",
pages: [
{
id: "intake",
blocks: [
{ id: "name", kind: "short_text", label: "Your name", required: true },
{ id: "email", kind: "email", label: "Work email", required: true },
{
id: "outcome",
kind: "long_text",
label: "What result do you need?",
required: true,
},
{ id: "target-date", kind: "date", label: "Target date" },
{
id: "documents",
kind: "file_upload",
label: "Briefs or reference files (PDF, DOCX, PNG or JPG)",
accept: [".pdf", ".doc", ".docx", ".png", ".jpg", ".jpeg"],
maxFiles: 5,
},
],
},
],
settings: { submitLabel: "Send project details" },
});
Keep the form and field IDs after you collect the first response. Fillo uses them as stored answer keys. You can change labels and help text without changing the IDs.
The React app controls the route, layout, styles and what happens after submit. Fillo handles the schema, validation, uploads and responses. The SDK renders React controls in the page. It does not use an iframe.
Send files straight to storage
The browser sends each file to the storage connected to the Fillo workspace. The Vite app does not proxy the file through its own server. Storage credentials do not go into the browser bundle.
For client files, connect Google Drive, Box, Amazon S3 or an S3-compatible bucket. Some eligible new workspaces can use temporary Fillo storage while they test. It accepts files up to 10 MiB, with 100 MiB available per workspace. Completed files expire after seven days. Connect your own storage before you use the form for client work.
Run the preview
The example still shows the form when no publishable key is present:
const key = import.meta.env.VITE_FILLO_KEY;
const fillo = key ? createClient({ key }) : null;
export function IntakeForm() {
return fillo ? (
<FilloForm form={intake} client={fillo} showTitle={false} />
) : (
<FilloForm form={intake} renderOnly showTitle={false} />
);
}
Preview mode lets you fill in the fields and see the success state. It does not send a response. File uploads stay disabled.
Run it locally:
git clone https://github.com/jacobfunch/fillo-vite-client-intake-starter.git
cd fillo-vite-client-intake-starter
npm install
npm run dev
Test before you share it
Check these cases before a client uses the form:
- Leave each required field empty and check the error.
- Select a file type that the form does not accept.
- Test the file count and storage size limits.
- Interrupt an upload and try it again.
- Submit a test response and find its files in Fillo.
- Use the form with a keyboard and on a phone.
- Delete the test response and check what happens to its files.
The repository contains the source, screenshots and a checklist for coding agents. The setup guide explains storage and publishing in more detail.
To collect responses with this example, start with Fillo.
Top comments (2)
Really nice approach. The detail that stood out to me is keeping field IDs stable once responses exist. It sounds minor, but treating those IDs as part of the data contract prevents a lot of pain later when labels or form copy change.
I also like the direct-to-storage upload model. Keeping large files out of the application server is a sensible architecture, especially for intake forms that may grow beyond a simple contact form.
One thing I’d be curious about: how does Fillo handle orphaned uploads when a user uploads files but abandons the form before submitting? Is there an automatic cleanup/lifecycle mechanism for connected S3-compatible storage?
Thanks! Yes, Fillo cleans these up automatically.
An unfinished upload is removed after 24 hours. If the upload finishes but the form is never submitted, Fillo keeps the file for seven days to support draft recovery, then deletes it from the connected storage.
For S3-compatible storage, Fillo aborts the multipart upload and checks that the object is gone. If cleanup fails, it keeps the record and tries again. Files attached to a submitted response are not affected.