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.
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 coloredFileit takes the CTA's text color instead, so it shows against the fill. It comes from the bestax extras (extras.css, orbestax.css, which includes them); with Bulma's CSS alone the input shows no focus ring. - When
Filerenders its ownField(that is, outside an existing one), the Field-levellabelprop is automatically associated with the file input viahtmlFor, so the input then has two labels (the Field label plus the wrapping CTA label); assistive technology reads both. An explicitlabelProps.htmlForoverrides the association and no id is generated. Inside a labeledField, thatField'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
hasNameits 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 infileName, such as a file uploaded earlier, reaches assistive technology only from there. - Add
aria-labelto the<input>for accessibility if your label is not plain text.
Related Components
Field: For labeled/grouped fields.Icon: For file icons.- Helper Props
Additional Resources
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | React.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). |
labelProps | React.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. |
isBoxed | boolean | false | Boxed file input. |
isFullwidth | boolean | false | Whether the file input expands to full width. |
isFullWidth | boolean | false | Deprecated. Use isFullwidth instead — isFullwidth wins if both are set. Whether the file input expands to full width. |
isRight | boolean | false | Position the CTA on the right (with hasName). |
isCentered | boolean | false | Center the file input within its container. |
hasName | boolean | false | Show 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. |
buttonLabel | React.ReactNode | — | Text on the file CTA button (defaults to "Choose a file…"). |
iconLeft | React.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. |
iconRight | React.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. |
className | string | — | Additional CSS classes to apply. |
inputClassName | string | — | Additional CSS classes for the <input>. |
fileName | string | — | 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). |
horizontal | boolean | false | Horizontal field layout. |
message | React.ReactNode | — | Help/validation message below the input. |
messageColor | 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger' | — | Bulma color for the message. |
fieldClassName | string | — | Additional CSS classes for the Field wrapper. |
ref | React.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 Variable | Sass Variable | Default |
|---|---|---|
--bulma-file-radius | $file-radius | var(--bulma-radius) |
--bulma-file-name-border-color | $file-name-border-color | var(--bulma-border) |
--bulma-file-name-border-style | $file-name-border-style | solid |
--bulma-file-name-border-width | $file-name-border-width | 1px 1px 1px 0 |
--bulma-file-name-max-width | $file-name-max-width | 16em |
--bulma-file-h | $file-h | var(--bulma-scheme-h) |
--bulma-file-s | $file-s | var(--bulma-scheme-s) |
--bulma-file-background-l | $file-background-l | var(--bulma-scheme-main-ter-l) |
--bulma-file-background-l-delta | $file-background-l-delta | 0% |
--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-l | var(--bulma-border-l) |
--bulma-file-border-l-delta | $file-border-l-delta | 0% |
--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-l | var(--bulma-text-strong-l) |
--bulma-file-name-color-l | $file-name-color-l | var(--bulma-text-strong-l) |
--bulma-file-color-l-delta | $file-color-l-delta | 0% |
--bulma-file-hover-color-l-delta | $file-hover-color-l-delta | -5% |
--bulma-file-active-color-l-delta | $file-active-color-l-delta | -10% |