beta

FileTrigger

A FileTrigger allows a user to access the file system with any pressable React Aria or React Spectrum component, or custom components built with usePress.

installyarn add react-aria-components
version1.0.0-beta.2
usageimport {FileTrigger} from 'react-aria-components'

Example#


import {FileTrigger, Button} from 'react-aria-components';

function Example(){
  let [file, setFile] = React.useState(null);

  return (
    <>
      <FileTrigger
        onSelect={(e) => {
          let files = Array.from(e);
          let filenames = files.map((file) => file.name);
          setFile(filenames);
        }}>
        <Button>Select a file</Button>
      </FileTrigger>
      {file && file}
    </>
  )
}
import {FileTrigger, Button} from 'react-aria-components';

function Example(){
  let [file, setFile] = React.useState(null);

  return (
    <>
      <FileTrigger
        onSelect={(e) => {
          let files = Array.from(e);
          let filenames = files.map((file) => file.name);
          setFile(filenames);
        }}>
        <Button>Select a file</Button>
      </FileTrigger>
      {file && file}
    </>
  )
}
import {
  Button,
  FileTrigger
} from 'react-aria-components';

function Example() {
  let [file, setFile] =
    React.useState(null);

  return (
    <>
      <FileTrigger
        onSelect={(
          e
        ) => {
          let files =
            Array.from(
              e
            );
          let filenames =
            files.map((
              file
            ) =>
              file.name
            );
          setFile(
            filenames
          );
        }}
      >
        <Button>
          Select a file
        </Button>
      </FileTrigger>
      {file && file}
    </>
  );
}
Show CSS
.react-aria-Button {
  --border-color: var(--spectrum-alias-border-color);
  --border-color-pressed: var(--spectrum-alias-border-color-down);
  --border-color-disabled: var(--spectrum-alias-border-color-disabled);
  --background-color: var(--spectrum-global-color-gray-50);
  --background-color-pressed: var(--spectrum-global-color-gray-100);
  --text-color: var(--spectrum-alias-text-color);
  --text-color-disabled: var(--spectrum-alias-text-color-disabled);
  --focus-ring-color: slateblue;

  color: var(--text-color);
  background: var(--background-color);
  border: 1px solid var(--border-color);
  border-radius: 4px;
  appearance: none;
  vertical-align: middle;
  font-size: 1rem;
  text-align: center;
  margin: 0;
  outline: none;
  padding: 6px 10px;

  &[data-pressed] {
    box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1);
    background: var(--background-color-pressed);
    border-color: var(--border-color-pressed);
  }

  &[data-focus-visible] {
    border-color: var(--focus-ring-color);
    box-shadow: 0 0 0 1px var(--focus-ring-color);
  }

  &[data-disabled] {
    border-color: var(--border-color-disabled);
    color: var(--text-color-disabled);
  }
}

@media (forced-colors: active) {
  .react-aria-Button {
    forced-color-adjust: none;
    --border-color: ButtonBorder;
    --border-color-pressed: ButtonBorder;
    --border-color-disabled: GrayText;
    --background-color: ButtonFace;
    --background-color-pressed: ButtonFace;
    --text-color: ButtonText;
    --text-color-disabled: GrayText;
    --focus-ring-color: Highlight;
  }
}
.react-aria-Button {
  --border-color: var(--spectrum-alias-border-color);
  --border-color-pressed: var(--spectrum-alias-border-color-down);
  --border-color-disabled: var(--spectrum-alias-border-color-disabled);
  --background-color: var(--spectrum-global-color-gray-50);
  --background-color-pressed: var(--spectrum-global-color-gray-100);
  --text-color: var(--spectrum-alias-text-color);
  --text-color-disabled: var(--spectrum-alias-text-color-disabled);
  --focus-ring-color: slateblue;

  color: var(--text-color);
  background: var(--background-color);
  border: 1px solid var(--border-color);
  border-radius: 4px;
  appearance: none;
  vertical-align: middle;
  font-size: 1rem;
  text-align: center;
  margin: 0;
  outline: none;
  padding: 6px 10px;

  &[data-pressed] {
    box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1);
    background: var(--background-color-pressed);
    border-color: var(--border-color-pressed);
  }

  &[data-focus-visible] {
    border-color: var(--focus-ring-color);
    box-shadow: 0 0 0 1px var(--focus-ring-color);
  }

  &[data-disabled] {
    border-color: var(--border-color-disabled);
    color: var(--text-color-disabled);
  }
}

@media (forced-colors: active) {
  .react-aria-Button {
    forced-color-adjust: none;
    --border-color: ButtonBorder;
    --border-color-pressed: ButtonBorder;
    --border-color-disabled: GrayText;
    --background-color: ButtonFace;
    --background-color-pressed: ButtonFace;
    --text-color: ButtonText;
    --text-color-disabled: GrayText;
    --focus-ring-color: Highlight;
  }
}
.react-aria-Button {
  --border-color: var(--spectrum-alias-border-color);
  --border-color-pressed: var(--spectrum-alias-border-color-down);
  --border-color-disabled: var(--spectrum-alias-border-color-disabled);
  --background-color: var(--spectrum-global-color-gray-50);
  --background-color-pressed: var(--spectrum-global-color-gray-100);
  --text-color: var(--spectrum-alias-text-color);
  --text-color-disabled: var(--spectrum-alias-text-color-disabled);
  --focus-ring-color: slateblue;

  color: var(--text-color);
  background: var(--background-color);
  border: 1px solid var(--border-color);
  border-radius: 4px;
  appearance: none;
  vertical-align: middle;
  font-size: 1rem;
  text-align: center;
  margin: 0;
  outline: none;
  padding: 6px 10px;

  &[data-pressed] {
    box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1);
    background: var(--background-color-pressed);
    border-color: var(--border-color-pressed);
  }

  &[data-focus-visible] {
    border-color: var(--focus-ring-color);
    box-shadow: 0 0 0 1px var(--focus-ring-color);
  }

  &[data-disabled] {
    border-color: var(--border-color-disabled);
    color: var(--text-color-disabled);
  }
}

@media (forced-colors: active) {
  .react-aria-Button {
    forced-color-adjust: none;
    --border-color: ButtonBorder;
    --border-color-pressed: ButtonBorder;
    --border-color-disabled: GrayText;
    --background-color: ButtonFace;
    --background-color-pressed: ButtonFace;
    --text-color: ButtonText;
    --text-color-disabled: GrayText;
    --focus-ring-color: Highlight;
  }
}

Features#


A file input can be created with an <input type=“file”> element, but this supports limited styling options and may not integrate well with the overall design of a website or application. To overcome this, FileTrigger extends the functionality of the standard file input element by working with a pressable child such as a Button to create accessible file inputs that can be style as needed.

  • Customizable – Works with any pressable React Aria or React Spectrum component, and custom components built with usePress.

Anatomy#


A FileTrigger wraps around a pressable child such as a button, and includes a visually hidden input element that allows the user to select files from their device.

import {FileTrigger, Button} from 'react-aria-components';

<FileTrigger>
  <Button />
</FileTrigger>
import {FileTrigger, Button} from 'react-aria-components';

<FileTrigger>
  <Button />
</FileTrigger>
import {
  Button,
  FileTrigger
} from 'react-aria-components';

<FileTrigger>
  <Button />
</FileTrigger>

If a visual label is not provided on the pressable child, then an aria-label or aria-labelledby prop must be passed to identify the file trigger to assistive technology.

Composed Components#

A FileTrigger can use the following components, which may also be used standalone or reused in other components.

Button
A button allows a user to perform an action with a mouse, touch, or keyboard.

Accepted file types#


By default, the file trigger will accept any file type. To support only certain file types, pass an array of the mime type of files via the acceptedFileTypes prop.

<FileTrigger acceptedFileTypes={['image/png']}>
  <Button>Select files</Button>
</FileTrigger>
<FileTrigger acceptedFileTypes={['image/png']}>
  <Button>Select files</Button>
</FileTrigger>
<FileTrigger
  acceptedFileTypes={[
    'image/png'
  ]}
>
  <Button>
    Select files
  </Button>
</FileTrigger>

Multiple files#


A file trigger can accept multiple files by passsing the allowsMultiple property.

<FileTrigger allowsMultiple>
  <Button>Upload your files</Button>
</FileTrigger>
<FileTrigger allowsMultiple>
  <Button>Upload your files</Button>
</FileTrigger>
<FileTrigger
  allowsMultiple
>
  <Button>
    Upload your files
  </Button>
</FileTrigger>

Media capture#


To specify the media capture mechanism to capture media on the spot, pass user for the user-facing camera or environment for the outward-facing camera via the defaultCamera prop.

This behavior only works on mobile devices. On desktop devices, it will open the file system like normal. Read more about capture.

<FileTrigger defaultCamera="environment">
  <Button>Open Camera</Button>
</FileTrigger>
<FileTrigger defaultCamera="environment">
  <Button>Open Camera</Button>
</FileTrigger>
<FileTrigger defaultCamera="environment">
  <Button>
    Open Camera
  </Button>
</FileTrigger>

Props#


NameTypeDescription
acceptedFileTypesArray<string>Specifies what mime type of files are allowed.
allowsMultiplebooleanWhether multiple files can be selected.
defaultCamera'user''environment'Specifies the use of a media capture mechanism to capture the media on the spot.
childrenReactNodeThe children of the component.
Events
NameTypeDescription
onSelect( (files: FileListnull )) => voidHandler when a user selects a file.

Styling#


FileTrigger#

The FileTrigger component does not render any element of its own so it does not support styling.

Button#

A Button can be targeted with the .react-aria-Button CSS selector, or by overriding with a custom className. It supports the following states:

NameCSS SelectorDescription
isHovered[data-hovered]Whether the button is currently hovered with a mouse.
isPressed[data-pressed]Whether the button is currently in a pressed state.
isFocused[data-focused]Whether the button is focused, either via a mouse or keyboard.
isFocusVisible[data-focus-visible]Whether the button is keyboard focused.
isDisabled[data-disabled]Whether the button is disabled.