gform-react
Back to Guides

File Inputs

type="file" stores the real File object, not the C:\fakepath string - with single and multiple support.

With a native <input type="file">, the field's value is the useless C:\fakepath\… string. gform-react fixes this: a GInput with type="file" stores the real File object (or File[] with multiple) in form state.

Single file

File inputs are intentionally uncontrolled - the DOM owns the FileList, so you don't pass a value. The current selection is reflected into state on change.

UploadCv.tsx
import { GForm, GInput, GValidator, type GValidators } from "gform-react";
 
interface CvForm {
  cv: File | null;
}
 
const validators: GValidators<CvForm> = {
  cv: new GValidator().withRequiredMessage("Please attach your CV"),
};
 
export default function UploadCv() {
  return (
    <GForm<CvForm>
      validators={validators}
      onSubmit={(state, e) => {
        e.preventDefault();
        const data = state.toFormData(); // includes the file
        // upload `data` via your service…
      }}
    >
      <GInput
        formKey="cv"
        type="file"
        required
        accept=".pdf,.doc,.docx"
        element={(input, props) => (
          <div>
            <input {...props} />
            {/* show the chosen file name from state */}
            {input.value && <small>{(input.value as File).name}</small>}
            {input.error && <small className="error">{input.errorText}</small>}
          </div>
        )}
      />
      <button>Upload</button>
    </GForm>
  );
}
  • Single file → state.cv.value is File | null.
  • With multiplestate.cv.value is File[].

Multiple files

Add the native multiple attribute; input.value becomes a File[].

<GInput
  formKey="attachments"
  type="file"
  multiple
  element={(input, props) => (
    <div>
      <input {...props} />
      <ul>
        {(input.value as File[] | null)?.map((file) => (
          <li key={file.name}>{file.name}</li>
        ))}
      </ul>
    </div>
  )}
/>
Uncontrolled by design

Because file inputs are uncontrolled, you can't programmatically set the selected files via value

  • the browser doesn't allow it. input.value always reflects the user's current selection.

Submitting files

state.toFormData() produces a FormData instance with file fields included automatically (via the underlying <form>), ready to POST to your endpoint or pass to a Server Action.

onSubmit={(state, e) => {
  e.preventDefault();
  fetch("/api/upload", { method: "POST", body: state.toFormData() });
}}

Try it live

Loading playground…