Skip to main content

File

Overview​

The File component provides a Bulma-styled file input, supporting color, size, boxed/fullwidth/align styles, icons, "has name", and filename display.

It is highly customizable for all file upload UI needs.


Import​

import { File, Icon } from '@allxsmith/bestax-bulma';

Usage​

File is a self-contained Bulma file widget. It detects whether it's already inside a Field and skips rendering its own field wrapper if so. For typical use, pass buttonLabel, iconLeft, and any modifier props (hasName, isBoxed, isFullwidth, isRight, isCentered, color, size) — Bulma doesn't document file inputs in addons or grouped layouts, so the convenience form covers every case. Use label only when you want an additional Bulma <label class="label"> rendered above the widget.

Default​

A basic file input. The buttonLabel prop sets the text on the CTA button, and iconLeft adds an icon for visual context.

<File
  buttonLabel="Choose a file…"
  iconLeft={<Icon name="upload" aria-hidden="true" />}
/>


With Filename Display​

Set hasName to show the name of the file the user picks. There is no name area before a pick, so pick a file below to see it appear. Until then the File carries Bulma's is-empty. With multiple, picking several files shows how many ("3 files"), and pickedFilesLabel words that count for another language. A reset of the input's form clears the name.

<File
  hasName
  buttonLabel="Choose a file…"
  iconLeft={<Icon name="upload" aria-hidden="true" />}
/>

Pass fileName to set the text yourself, for a file chosen earlier or your own wording. It takes over from the picked file's name, so update it from onChange if it should follow the pick. The examples below use it to show a name without a pick.

<File
  hasName
  fileName="resume.pdf"
  buttonLabel="Choose a file…"
  iconLeft={<Icon name="upload" aria-hidden="true" />}
/>


CTA on Right​

Combine isRight with hasName to put the CTA button on the right and the filename on the left.

<File
  hasName
  isRight
  fileName="contract.pdf"
  buttonLabel="Choose a file…"
  iconLeft={<Icon name="upload" aria-hidden="true" />}
/>


Full Width​

isFullwidth makes the file input take the full width of its container — pairs well with hasName so the filename area expands.

<File
  hasName
  isFullwidth
  fileName="picture.png"
  buttonLabel="Choose a file…"
  iconLeft={<Icon name="upload" aria-hidden="true" />}
/>


Boxed​

isBoxed stacks the icon over the text into a square box.

<File
  isBoxed
  buttonLabel="Choose a file…"
  iconLeft={<Icon name="upload" aria-hidden="true" />}
/>


Boxed with Name​

Combine isBoxed and hasName for a boxed widget that also shows the filename.

<File
  isBoxed
  hasName
  fileName="holiday.jpg"
  buttonLabel="Choose a file…"
  iconLeft={<Icon name="upload" aria-hidden="true" />}
/>


Colors​

The color prop applies Bulma color modifiers. The four examples below match the combinations Bulma's docs show.

<>
  <File
    color="primary"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    color="info"
    hasName
    fileName="resume.pdf"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    color="warning"
    isBoxed
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="cloud-upload-alt" aria-hidden="true" />}
  />
  <File
    color="danger"
    isBoxed
    hasName
    fileName="resume.pdf"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="cloud-upload-alt" aria-hidden="true" />}
  />
</>


Sizes​

The size prop controls the file input's size.

<>
  <File
    size="small"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    size="medium"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    size="large"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
</>


Sizes with Name​

Combine size with hasName to scale the filename display alongside the button.

<>
  <File
    size="small"
    hasName
    fileName="sample.txt"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    hasName
    fileName="sample.txt"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    size="medium"
    hasName
    fileName="sample.txt"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    size="large"
    hasName
    fileName="sample.txt"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
</>


Sizes with Boxed​

Combine size with isBoxed for boxed file inputs at every size.

<>
  <File
    size="small"
    isBoxed
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    isBoxed
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    size="medium"
    isBoxed
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    size="large"
    isBoxed
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
</>


Sizes with Boxed and Name​

For a boxed file input that also shows the filename, combine isBoxed, hasName, and size.

<>
  <File
    size="small"
    isBoxed
    hasName
    fileName="summary.docx"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    isBoxed
    hasName
    fileName="summary.docx"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    size="medium"
    isBoxed
    hasName
    fileName="summary.docx"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
  <File
    size="large"
    isBoxed
    hasName
    fileName="summary.docx"
    buttonLabel="Choose a file…"
    iconLeft={<Icon name="upload" aria-hidden="true" />}
  />
</>


Alignment: Centered​

isCentered centers the widget within its parent container.

<File
  color="info"
  isCentered
  isBoxed
  hasName
  fileName="centered.pdf"
  buttonLabel="Choose a file…"
  iconLeft={<Icon name="upload" aria-hidden="true" />}
/>


Alignment: Right​

isRight aligns the widget to the right of its parent.

<File
  color="primary"
  isRight
  hasName
  fileName="right.pdf"
  buttonLabel="Choose a file…"
  iconLeft={<Icon name="upload" aria-hidden="true" />}
/>


Context-Aware Rendering​

The File component is context-aware: it detects whether it is already inside a Field and skips rendering its own field wrapper if so. Use label to add a Bulma <label class="label"> above the widget; use buttonLabel to set the CTA text.

note

File renders no Control of its own (it is a self-contained widget). The "With Field and Control Wrappers" example below shows that wrapping File in a Control is harmless but doesn't change its rendering.

Default (with label)​

The simplest usage — label adds a Field label above the widget.

<File
  label="Document"
  buttonLabel="Choose a file…"
  iconLeft={<Icon name="upload" aria-hidden="true" />}
/>


With Field Wrapper​

For manual layout control (e.g., horizontal forms), wrap in Field. The component detects it's inside a Field and skips rendering its own.

A labeled Field that holds the File directly names its input with no extra wiring. In a horizontal form the label sits on the outer Field and the File in an inner one, which starts its own scope, so these examples wire the label by hand with labelProps={{ htmlFor }} and a matching id.

function example() {
  return (
    <Field
      horizontal
      label="Document"
      labelProps={{ htmlFor: 'document-field' }}
    >
      <Field.Body>
        <Field>
          <File
            id="document-field"
            buttonLabel="Choose a file…"
            iconLeft={<Icon name="upload" aria-hidden="true" />}
          />
        </Field>
      </Field.Body>
    </Field>
  );
}


With Field and Control Wrappers​

For full manual composition, wrap in both Field and Control. The outer Field is detected and File's own Field wrapper is skipped.

function example() {
  return (
    <Field
      horizontal
      label="Document"
      labelProps={{ htmlFor: 'document-field-control' }}
    >
      <Field.Body>
        <Field>
          <Control iconLeftName="paperclip">
            <File
              id="document-field-control"
              buttonLabel="Choose a file…"
              iconLeft={<Icon name="upload" aria-hidden="true" />}
            />
          </Control>
        </Field>
      </Field.Body>
    </Field>
  );
}

Inside a Control with no Field around it, File renders no Field of its own either, unless you give it label, message, horizontal or fieldClassName. Those need a Field, so with any of them it keeps its own Field inside the Control and warns in development. Wrap the Control in a Field, as above, and set the label, horizontal and class name on that Field instead.


Accessibility​

  • The root is a <div class="file"> with a nested <label> and <input type="file">.
  • The label is always clickable.
  • The focus ring shows for keyboard focus only, drawn inside the CTA in Bulma's focus color, width and style (--bulma-focus-h, --bulma-focus-s, --bulma-focus-l, --bulma-focus-width, --bulma-focus-style). On a colored File it takes the CTA's text color instead, so it shows against the fill. It comes from the bestax extras (extras.css, or bestax.css, which includes them); with Bulma's CSS alone the input shows no focus ring.
  • When File renders its own Field (that is, outside an existing one), the Field-level label prop is automatically associated with the file input via htmlFor, so the input then has two labels (the Field label plus the wrapping CTA label); assistive technology reads both. An explicit labelProps.htmlFor overrides the association and no id is generated. Inside a labeled Field, that Field's label is associated with the file input the same way, so the input again has two labels.
  • The name area sits inside the CTA's label, so with hasName its text joins the input's accessible name ("Choose a file… resume.pdf"). Browsers can also expose a picked file on the input itself, so a screen reader may read the name twice. It stays in the label because text you pass in fileName, such as a file uploaded earlier, reaches assistive technology only from there.
  • Add aria-label to the <input> for accessibility if your label is not plain text.


Additional Resources​


Props​

PropTypeDefaultDescription
labelReact.ReactNode—Field label. Automatically associated with the file input via htmlFor — uses your id when provided, otherwise a generated one. The input then has two labels (this one plus the wrapping file-label); assistive tech reads both. Dropped inside an outer Field, whose own label associates instead when that Field generates a target id (not grouped/hasAddons, no explicit labelProps.htmlFor).
labelPropsReact.LabelHTMLAttributes<HTMLLabelElement> & { [key: string]: unknown; }—Props for the label element. An explicit htmlFor here overrides the automatic association (no id is generated then).
color'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger' | 'black' | 'dark' | 'light' | 'white'—Bulma color modifier for the file input.
size'small' | 'medium' | 'large'—Size modifier for the file input.
isBoxedbooleanfalseBoxed file input.
isFullwidthbooleanfalseWhether the file input expands to full width.
isFullWidthbooleanfalseDeprecated. Use isFullwidth instead — isFullwidth wins if both are set. Whether the file input expands to full width.
isRightbooleanfalsePosition the CTA on the right (with hasName).
isCenteredbooleanfalseCenter the file input within its container.
hasNamebooleanfalseShow a file name area. Without fileName it shows what the user picked: the file's name, or a count when multiple lets them pick several (see pickedFilesLabel). Before a pick there is no name area, and the root takes Bulma's is-empty. A reset of the input's form clears the name, but clearing the input from code fires no change event and leaves it showing, so pass fileName to control the text then.
buttonLabelReact.ReactNode—Text on the file CTA button (defaults to "Choose a file…").
iconLeftReact.ReactNode—Left icon element. It renders inside the <label> that names the file input, beside buttonLabel, so leave an Icon here unnamed (<Icon name="upload" />): it is then aria-hidden, while an ariaLabel would join the input's accessible name.
iconRightReact.ReactNode—Right icon element. Sits inside the same <label> as iconLeft, so the same applies: an Icon here with an ariaLabel joins the input's accessible name.
classNamestring—Additional CSS classes to apply.
inputClassNamestring—Additional CSS classes for the <input>.
fileNamestring—Text for the file name area (with hasName), for a file chosen earlier or your own wording. Setting it takes over from the picked file's name; an empty string shows no name area.
pickedFilesLabel(count: number) => string—Builds the text hasName shows when several files are picked, from their count, for localization. Default: `${count} files`. A single pick shows its file's name.
labelSize'small' | 'normal' | 'medium' | 'large'—Size for the label (used in horizontal layouts).
horizontalbooleanfalseHorizontal field layout.
messageReact.ReactNode—Help/validation message below the input.
messageColor'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'—Bulma color for the message.
fieldClassNamestring—Additional CSS classes for the Field wrapper.
refReact.Ref<HTMLInputElement>—Forwarded to the underlying element.
...All standard <input> attributes and Bulma helper props—See Helper Props

CSS & Sass Variables​

File registers these variables on its own .file element. Override them there (or via className) — a value set on an ancestor is only inherited, and loses to the component-level declaration. See Theme.

CSS VariableSass VariableDefault
--bulma-file-radius$file-radiusvar(--bulma-radius)
--bulma-file-name-border-color$file-name-border-colorvar(--bulma-border)
--bulma-file-name-border-style$file-name-border-stylesolid
--bulma-file-name-border-width$file-name-border-width1px 1px 1px 0
--bulma-file-name-max-width$file-name-max-width16em
--bulma-file-h$file-hvar(--bulma-scheme-h)
--bulma-file-s$file-svar(--bulma-scheme-s)
--bulma-file-background-l$file-background-lvar(--bulma-scheme-main-ter-l)
--bulma-file-background-l-delta$file-background-l-delta0%
--bulma-file-hover-background-l-delta$file-hover-background-l-delta-5%
--bulma-file-active-background-l-delta$file-active-background-l-delta-10%
--bulma-file-border-l$file-border-lvar(--bulma-border-l)
--bulma-file-border-l-delta$file-border-l-delta0%
--bulma-file-hover-border-l-delta$file-hover-border-l-delta-10%
--bulma-file-active-border-l-delta$file-active-border-l-delta-20%
--bulma-file-cta-color-l$file-cta-color-lvar(--bulma-text-strong-l)
--bulma-file-name-color-l$file-name-color-lvar(--bulma-text-strong-l)
--bulma-file-color-l-delta$file-color-l-delta0%
--bulma-file-hover-color-l-delta$file-hover-color-l-delta-5%
--bulma-file-active-color-l-delta$file-active-color-l-delta-10%