S
Documentation

File Upload

A drag-and-drop file upload field that supports single or multiple files, MIME-type filtering, per-file and total size caps, and returns rich metadata alongside the raw File objects for your upload handler.

Schema Example

filejson
{
  id: "resume",
  type: "file",
  label: "Upload your résumé",
  description: "PDF or Word document, max 5 MB",
  required: true,
  accept: ".pdf,.doc,.docx",
  maxFiles: 1,
  maxFileSize: 5242880
}

Available Options

PropertyTypeDefaultDescription
acceptstringComma-separated MIME types or file extensions passed directly to the native <input accept> attribute. E.g. "image/*,.pdf".
maxFilesnumber1Maximum number of files the respondent can select. Values greater than 1 enable multi-file selection. Exceeding the limit triggers a validation error.
maxFileSizenumberMaximum allowed size per file in bytes. E.g. 5 * 1024 * 1024 for 5 MB. Oversized files are listed in the validation error message.
maxTotalSizenumberMaximum combined size of all selected files in bytes. Useful when limiting total upload payload.
requiredbooleanfalseWhether at least one file must be selected before the survey can be submitted.
labelstringThe question text displayed to the user.
descriptionstringOptional helper text shown below the label (e.g. allowed formats, size limits).
visibleIfstringConditional expression to show/hide this question.
validationValidationRule[]Custom validation rules. Use type: 'custom' for advanced file checks (e.g. dimensions, virus scan status).

Answer Format

Returns a FileAnswer[] array — one entry per selected file. The file property holds the raw File object needed by upload APIs; the rest are plain serialisable values.

FileAnswertypescript
interface FileAnswer {
  /** Original filename */
  name: string;
  /** File size in bytes */
  size: number;
  /** MIME type, e.g. "application/pdf" */
  type: string;
  /** Last-modified timestamp (ms since epoch) */
  lastModified: number;
  /** The raw File object — browser session only, not JSON-serialisable */
  file: File;
}

When a single file is required, the answer is still an array with one element: answers['resume'][0].file.

Configurations

Common patterns for the file upload question:

File configurationsjson
// Single image upload (2 MB cap)
{
  id: "avatar",
  type: "file",
  label: "Profile picture",
  accept: "image/*",
  maxFiles: 1,
  maxFileSize: 2 * 1024 * 1024,
  required: true
}

// Multi-file attachment (up to 5 files, 10 MB each, 20 MB total)
{
  id: "attachments",
  type: "file",
  label: "Supporting documents",
  description: "PDF, Word or images — up to 5 files, 10 MB each",
  accept: ".pdf,.doc,.docx,image/*",
  maxFiles: 5,
  maxFileSize: 10 * 1024 * 1024,
  maxTotalSize: 20 * 1024 * 1024
}

// Video upload (no file count or size restriction, type-only filter)
{
  id: "demo-video",
  type: "file",
  label: "Demo video",
  accept: "video/*",
  maxFiles: 1
}

// Any file, up to 10 at once
{
  id: "assets",
  type: "file",
  label: "Project assets",
  maxFiles: 10,
  maxTotalSize: 50 * 1024 * 1024
}

Built-in File Validation

The validation engine automatically enforces maxFiles, maxFileSize, and maxTotalSize constraints. You can still layer additional custom rules on top for use-case-specific checks:

Custom validator on a file questiontypescript
{
  id: "spreadsheet",
  type: "file",
  label: "Upload data export",
  accept: ".csv,.xlsx",
  maxFiles: 1,
  maxFileSize: 20 * 1024 * 1024,
  required: true,
  validation: [
    {
      type: "custom",
      validator: (value) => {
        const files = value as FileAnswer[];
        if (!files?.length) return true; // required rule handles empty
        const name = files[0].name.toLowerCase();
        if (!name.endsWith(".csv") && !name.endsWith(".xlsx")) {
          return "Only .csv and .xlsx files are accepted";
        }
        return true;
      },
    },
  ],
}

Handling Uploads in onSubmit

The onSubmit callback receives the full SurveyAnswers map. Cast the file question's value to FileAnswer[] to access the raw File objects, then post them via FormData:

ApplicationForm.tsxtsx
import { SurveyRenderer } from 'react-minimal-survey-builder';
import type { SurveySchema, FileAnswer } from 'react-minimal-survey-builder';

const uploadSchema: SurveySchema = {
  id: 'upload-form',
  title: 'Submit your application',
  pages: [
    {
      id: 'details',
      questions: [
        {
          id: 'full-name',
          type: 'text',
          label: 'Full name',
          required: true,
        },
        {
          id: 'resume',
          type: 'file',
          label: 'Résumé / CV',
          description: 'PDF only, max 5 MB',
          accept: '.pdf',
          maxFiles: 1,
          maxFileSize: 5 * 1024 * 1024,
          required: true,
        },
        {
          id: 'portfolio',
          type: 'file',
          label: 'Portfolio samples',
          description: 'Up to 3 files — images or PDFs, max 10 MB each',
          accept: 'image/*,.pdf',
          maxFiles: 3,
          maxFileSize: 10 * 1024 * 1024,
        },
      ],
    },
  ],
};

function ApplicationForm() {
  return (
    <SurveyRenderer
      schema={uploadSchema}
      options={{
        onSubmit: async (answers) => {
          const formData = new FormData();

          // Plain text answers
          formData.append('name', answers['full-name'] as string);

          // File answers — extract the raw File objects
          const resume = answers['resume'] as FileAnswer[];
          if (resume?.length) {
            formData.append('resume', resume[0].file, resume[0].name);
          }

          const portfolio = answers['portfolio'] as FileAnswer[];
          portfolio?.forEach((f, i) => {
            formData.append(`portfolio[${i}]`, f.file, f.name);
          });

          await fetch('/api/applications', { method: 'POST', body: formData });
        },
      }}
    />
  );
}

Using the validators Option

For cross-question logic or async checks (e.g. server-side virus scan), use the validators map on useSurvey / SurveyRenderer. The third parameter gives access to all current answers:

Cross-question file validationtsx
<SurveyRenderer
  schema={schema}
  options={{
    validators: {
      resume: (value, _question, answers) => {
        const files = value as FileAnswer[];
        if (!files?.length) return null;
        // Require PDF if applicant is external
        if (answers['applicant-type'] === 'external') {
          const nonPdf = files.filter((f) => f.type !== 'application/pdf');
          if (nonPdf.length > 0) return 'External applicants must upload PDF only';
        }
        return null;
      },
    },
    onSubmit: async (answers) => { /* ... */ },
  }}
/>

💡 File objects are session-only

The file property of each FileAnswer is a live browser File object. It cannot be JSON-serialised or persisted across page reloads — upload it in onSubmit before the session ends. The other properties (name, size, type, lastModified) are plain values and can be logged or sent safely.