Skip to main content
Menu
On this page

Build a client intake form with file uploads

Collect project context and reference files in one client intake flow, send uploads browser-direct to connected storage, and review one complete response.

A client intake form fails when it collects contact details but leaves the actual brief in email, the documents in a transfer link, and the next action in somebody's memory. The useful unit is one reviewable intake: who the client is, what outcome they need, when they need it, and which files support the request.

This guide builds that flow with a typed Fillo schema. The form can render inside a client portal or on a hosted page. Files upload browser-direct to the active storage destination, while the accepted response keeps the answers, file metadata, and provider references together.

Decide what the first working session needs

Do not turn intake into a complete discovery workshop. Ask only for information that changes what the team does next:

  • The client and a reliable contact address.
  • The outcome they expect, in their own words.
  • A target date when timing affects scope.
  • Briefs, screenshots, or reference documents the team must inspect.

Questions about budget, stakeholders, or project type belong in the first version only when somebody will use those answers to route or prepare the work.

Define the intake schema

Create the form at module scope so its identity does not change between renders.

tsx
// src/IntakeForm.tsx
import { createClient, defineForm, FilloForm } from "@usefillo/react";

const intake = defineForm({
  id: "client-intake",
  title: "Tell us about your project",
  description: "Share the context we need before the first working session.",
  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 should be different when this work is done?",
          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" },
});

const fillo = createClient({ key: import.meta.env.VITE_FILLO_KEY });

export function IntakeForm() {
  return <FilloForm form={intake} client={fillo} />;
}

The upload label states the accepted formats before the file picker opens. Keep the formats and count aligned with what the team can actually review.

In a claimed workspace that permits publishable-key sync, the first render stages the code-defined form. Review and publish the draft before sharing the route with a client.

Choose where files should live

For durable collection, connect storage once at the workspace level:

  • Google Drive
  • Box
  • Amazon S3
  • An S3-compatible bucket such as Cloudflare R2

The browser sends bytes directly to the connected provider. Fillo opens a scoped upload session, verifies completion on the server, and accepts only the resulting file reference with the response. Provider credentials and workspace secrets remain server-side.

Eligible newly provisioned workspaces can evaluate an upload flow before connecting durable storage. The temporary transit lane is limited to 10 MiB per file and 100 MiB per workspace, and completed transit files expire after seven days. Connect customer-owned storage before using the form as a durable client document workflow.

See file uploads for provider setup, upload limits, cleanup, and recovery behavior.

Put the form in the right surface

Use a hosted page when the client needs a simple standalone link. Render the native React or DOM form inside a client portal when the application already knows who the client is and what account the request belongs to.

For a signed-in portal, pass verified respondent identity from the host server. It can attach the client ID and contact details to the response without asking for them again. Identity is still not authorization: the host application decides who may open the route, and the provider controls access to durable files.

Fillo handles the form and response workflow. It does not claim to replace a complete client portal with messaging, billing, tasks, and document collaboration.

Keep the handoff attached to the response

After Fillo accepts the response, choose the system that owns the next action:

  • Review it in the response workspace with the file references beside the answers.
  • Export accepted responses to CSV.
  • Deliver a signed webhook to a project, CRM, or case-management system.
  • Use an integration when the destination already owns the operational workflow.

Do not send the file bytes again in every webhook. Use the response and provider references according to the destination's access policy.

Test the failures a real client will hit

Before sharing the intake, verify more than the happy path:

  1. Submit without the required contact or outcome.
  2. Choose a disallowed file type.
  3. Exceed the configured file count or active provider limit.
  4. Interrupt an upload, reconnect, and retry.
  5. Submit successfully and find the response plus file reference.
  6. Open the durable file as an authorized workspace member.
  7. Delete a test response and confirm the expected provider cleanup.
  8. Repeat the flow on a narrow screen and with keyboard-only navigation.

If the client must upload large files or resume after a dropped connection, test with the exact provider and browser combination the production workflow will use.

Start from working sources

Clone the Vite React client-intake starter for the complete example and environment setup. Or open the editable client intake template to begin with a builder-managed form and live local preview.

Updated

Was this page helpful?