---
title: File Picker
related:
  - title: Uploader
    path: uploader.md
  - title: Input
    path: input.md
---
QFile is a component which handles the user interaction for picking file(s).

> [!TIP]
> If you also want a component to handle the upload for you, please consider using [QUploader](uploader.md) instead.

## QFile API

### Props

- `name` (string, optional)
  Used to specify the name of the control; Useful if dealing with forms; If not specified, it takes the value of 'for' prop, if it exists
  Examples: `'car_id'`
- `multiple` (boolean, optional)
  Allow multiple file uploads
- `accept` (string, optional)
  Comma separated list of unique file type specifiers. Maps to 'accept' attribute of native input type=file element
  Examples:
    - `'.jpg, .pdf, image/*'`
    - `'image/jpeg, .pdf'`
- `capture` (string, optional)
  Optionally, specify that a new file should be captured, and which device should be used to capture that new media of a type defined by the 'accept' prop. Maps to 'capture' attribute of native input type=file element
  Accepts: `'user'`, `'environment'`
- `max-file-size` (number | string, optional)
  Maximum size of individual file in bytes
  Examples: `1024`, `'1048576'`
- `max-total-size` (number | string, optional)
  Maximum size of all files combined in bytes
- `max-files` (number | string, optional)
  Maximum number of files to contain
- `filter` (Function, optional)
  Custom filter for added files; Only files that pass this filter will be added to the queue and uploaded; For best performance, reference it from your scope and do not define it inline
  Function signature: `(files?: any[]) => any[]`
  Examples: `files => files.filter(file => file.size === 1024)`
  Params:
    - `files` (any[], optional)
      Candidate files to be added to queue
  Returns: `any[]`
    Filtered files to be added to queue
- `model-value` (File | FileList | any[], required, syncable)
  Model of the component; Must be FileList or Array if using 'multiple' prop; Either use this property (along with a listener for 'update:modelValue' event) OR use v-model directive
  Examples: `v-model="myModel"`
- `error` (boolean, optional), default `null`
  Does field have validation errors?; Setting it to a Boolean (even false) reserves the bottom space for the error message so the layout does not shift when the error appears; leave it at null (the default) when not using external validation, or use 'hide-bottom-space' to not reserve the space
- `error-message` (string, optional)
  Validation error message (gets displayed only if 'error' is set to 'true')
  Examples: `'Username must have at least 5 characters'`
- `no-error-icon` (boolean, optional)
  Hide error icon when there is an error
- `rules` (any[], optional)
  Array of Functions/Strings; If String, then it must be a name of one of the embedded validation rules
  Examples:
    - `[val => val.length <= 3 || 'Please use maximum 3 characters']`
    - `['fulltime']`
    - `[(val, rules) => rules.email(val) || 'Please enter a valid email address']`
- `reactive-rules` (boolean, optional)
  By default a change in the rules does not trigger a new validation until the model changes; If set to true then a change in the rules will trigger a validation; Has a performance penalty, so use it only when you really need it
- `lazy-rules` (boolean | string, optional), default `false`
  If set to boolean true then it checks validation status against the 'rules' only when field loses focus; while an error is displayed it re-checks on each model change so the error clears as soon as the value becomes valid; If set to 'ondemand' then it will trigger only when component's validate() method is manually called or when the wrapper QForm submits itself
  Accepts: `true`, `false`, `'ondemand'`
- `label` (string, optional)
  A text label that will “float” up above the input field, once the field gets focus
  Examples: `'Username'`
- `stack-label` (boolean, optional)
  Label will be always shown above the field regardless of field content (if any)
- `hint` (string, optional)
  Helper (hint) text which gets placed below your wrapped form component
  Examples: `'Fill in between 3 and 12 characters'`
- `hide-hint` (boolean, optional)
  Hide the helper (hint) text when field doesn't have focus
- `prefix` (string, optional)
  Prefix
  Examples: `'$'`
- `suffix` (string, optional)
  Suffix
  Examples: `'@gmail.com'`
- `label-color` (string, optional)
  Color name for the label from the Quasar Color Palette; Overrides the 'color' prop; The difference from 'color' prop is that the label will always have this color, even when field is not focused
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `color` (string, optional)
  Color name for component from the Quasar Color Palette
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `bg-color` (string, optional)
  Color name for component from the Quasar Color Palette
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `dark` (boolean, optional), default `null`
  Notify the component that the background is a dark color
- `loading` (boolean, optional)
  Signals the user a process is in progress by displaying a spinner; Spinner can be customized by using the 'loading' slot.
- `clearable` (boolean, optional)
  Appends clearable icon when a value (not undefined or null) is set; When clicked, model becomes null
- `clear-icon` (string, optional)
  Custom icon to use for the clear button when using along with 'clearable' prop
  Examples: `'close'`
- `filled` (boolean, optional)
  Use 'filled' design for the field
- `outlined` (boolean, optional)
  Use 'outlined' design for the field
- `borderless` (boolean, optional)
  Use 'borderless' design for the field
- `standout` (boolean | string, optional)
  Use 'standout' design for the field; Specifies classes to be applied when focused (overriding default ones); a text color class in there also colors the content and the decorators
  Examples: `true`, `'bg-primary text-white'`
- `label-slot` (boolean, optional)
  Enables label slot; You need to set it to force use of the 'label' slot if the 'label' prop is not set
- `bottom-slots` (boolean, optional)
  Enables bottom slots ('error', 'hint', 'counter')
- `hide-bottom-space` (boolean, optional)
  Do not reserve space for hint/error/counter anymore when these are not used; As a result, it also disables the animation for those
- `counter` (boolean, optional)
  Show an automatic counter on bottom right
- `rounded` (boolean, optional)
  Applies a small standard border-radius for a squared shape of the component
- `square` (boolean, optional)
  Remove border-radius so borders are squared; Overrides 'rounded' prop
- `dense` (boolean, optional)
  Dense mode; occupies less space
- `item-aligned` (boolean, optional)
  Match inner content alignment to that of QItem
- `disable` (boolean, optional)
  Put component in disabled mode
- `readonly` (boolean, optional)
  Put component in readonly mode
- `autofocus` (boolean, optional)
  Focus field on initial component render
- `for` (string, optional)
  Used to specify the 'id' of the control and also the 'for' attribute of the label that wraps it; If no 'name' prop is specified, then it is used for this attribute as well
  Examples: `'myFieldsId'`
- `append` (boolean, optional)
  Append file(s) to current model rather than replacing them; Has effect only when using 'multiple' mode
- `display-value` (number | string, optional)
  Override default selection string, if not using 'file' or 'selected' scoped slots and if not using 'use-chips' prop
  Examples:
    - `'Options: x, y, z'`
- `use-chips` (boolean, optional)
  Use QChip to show picked files
- `counter-label` (Function, optional)
  Label for the counter; The 'counter' prop is necessary to enable this one
  Function signature: `(props?: object) => string`
  Examples:
    - `(totalSize, filesNumber, maxFiles) => `${ filesNumber }${ maxFiles !== void 0 ? ' / ' + maxFiles : '' } (${ totalSize })``
  Params:
    - `props` (object, optional)
      Object containing counter label information
      Object shape:
        - `totalSize` (string, required)
          The total size of files in human readable format
          Examples: `'1.42MB'`
        - `filesNumber` (number, required)
          Number of picked files
        - `maxFiles` (number | string, required)
          Maximum number of files (same as 'max-files' prop, if specified); When 'max-files' is not specified, this has 'void 0' as value
  Returns: `string`
    String to display for the counter label
- `tabindex` (number | string, optional), default `0`
  Tabindex HTML attribute value
  Examples: `100`, `'0'`
- `input-class` (string | any[] | object, optional)
  Class definitions to be attributed to the underlying selection container
  Examples: `'my-special-class'`, `{ 'my-special-class': true }`
- `input-style` (string | any[] | object, optional)
  Style definitions to be attributed to the underlying selection container
  Examples: `'background-color: #ff0000'`, `{ backgroundColor: '#ff0000' }`

### Computed Props

- `hasError` (boolean, optional)
  Whether the component is in error state
- `nativeEl` (Element, optional)
  The native input DOM Element

### Methods

- `pickFiles(evt?: Event): void`
  Trigger file pick; Must be called as a direct consequence of user interaction (eg. in a click handler), due to browsers security policy
  Params:
    - `evt` (Event, optional)
      JS event object
- `addFiles(files: any[] | FileList): void`
  Add files programmatically
  Params:
    - `files` (any[] | FileList, required)
      Array of files (instances of File) or FileList
- `resetValidation(): void`
  Reset validation status
- `validate(value?: any): boolean | Promise<boolean>`
  Trigger a validation
  Params:
    - `value` (any, optional)
      Optional value to validate against
  Returns: `boolean | Promise<boolean>`
    True/false if no async rules, otherwise a Promise with the outcome (true -> validation was a success, false -> invalid models detected)
    Examples: `true`, `validate().then(outcome => { ... })`
- `focus(): void`
  Focus component
- `blur(): void`
  Blur component (lose focus)
- `removeAtIndex(index: number): void`
  Remove file located at specific index in the model
  Params:
    - `index` (number, required)
      Index at which to remove selection
- `removeFile(file: File): void`
  Remove specified file from the model
  Params:
    - `file` (File, required)
      File to remove (instance of File)
- `getNativeElement(): Element`
  DEPRECATED; Access 'nativeEl' directly; Gets the native input DOM Element
  Returns: `Element`
    The underlying native input DOM Element

### Events

- `@rejected`
  Emitted after files are picked and some do not pass the validation props (accept, max-file-size, max-total-size, filter, etc)
  Params:
    - `rejectedEntries` (any[], optional)
      Array of { failedPropValidation: string, file: File } Objects for files that do not pass the validation
- `@update:model-value`
  Emitted when the component needs to change the model; Is also used by v-model
  Params:
    - `value` (any, required)
      New model value
- `@focus`
  Emitted when component gets focused
  Params:
    - `evt` (Event, optional)
      JS event object
- `@blur`
  Emitted when component loses focus
  Params:
    - `evt` (Event, optional)
      JS event object
- `@clear`
  When using the 'clearable' property, this event is emitted when the clear icon is clicked
  Params:
    - `value` (any, optional)
      The previous value before clearing it

### Slots

- `#default`
  Field main content
- `#prepend`
  Prepend inner field; Suggestions: QIcon, QBtn
- `#append`
  Append to inner field; Suggestions: QIcon, QBtn
- `#before`
  Prepend outer field; Suggestions: QIcon, QBtn
- `#after`
  Append outer field; Suggestions: QIcon, QBtn
- `#label`
  Slot for label; Used only if 'label-slot' prop is set or the 'label' prop is set; When it is used the text in the 'label' prop is ignored
- `#error`
  Slot for errors; Enabled only if 'bottom-slots' prop is used; Suggestion: <div>
- `#hint`
  Slot for hint text; Enabled only if 'bottom-slots' prop is used; Suggestion: <div>
- `#counter`
  Slot for counter text; Enabled only if 'bottom-slots' prop is used; Suggestion: <div>
- `#loading`
  Override default spinner when component is in loading mode; Use in conjunction with 'loading' prop

### Scoped Slots

- `#file`
  Override default node to render a file from the user picked list
  Scope:
    - `index` (number, optional)
      Selection index
    - `file` (File, optional)
      File object
    - `ref` (ComponentInstance, optional)
      Reference to the QFile component
- `#selected`
  Override default selection slot; Suggestion: QChip
  Scope:
    - `files` (any[], optional)
      Array of File objects
    - `ref` (ComponentInstance, optional)
      Reference to the QFile component

## Design

> [!NOTE]
> For your QFile you can use only one of the main designs (`filled`, `outlined`, `standout`, `borderless`). You cannot use multiple as they are self-exclusive.

Example "Design Overview":

```vue
<template>
  <div style="max-width: 300px">
    <div class="q-gutter-md">
      <q-file v-model="model" label="Standard" />

      <q-file filled v-model="model" label="Filled" />

      <q-file outlined v-model="model" label="Outlined" />

      <q-file standout v-model="model" label="Standout" />

      <q-file
        standout="bg-teal text-white"
        v-model="model"
        label="Custom standout"
      />

      <q-file borderless v-model="model" label="Borderless" />

      <q-file rounded filled v-model="model" label="Rounded filled" />

      <q-file rounded outlined v-model="model" label="Rounded outlined" />

      <q-file rounded standout v-model="model" label="Rounded standout" />

      <q-file square filled v-model="model" label="Square filled" />

      <q-file square outlined v-model="model" label="Square outlined" />

      <q-file square standout v-model="model" label="Square standout" />
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const model = ref(null)
</script>
```

### Decorators

```vue
<template>
  <div class="q-gutter-md" style="max-width: 300px">
    <q-file filled v-model="model" label="Label (stacked)" stack-label />

    <q-file outlined v-model="model">
      <template #prepend>
        <q-icon name="attach_file" />
      </template>
    </q-file>

    <q-file standout v-model="model">
      <template #append>
        <q-avatar>
          <img
            alt="Quasar logo"
            src="https://cdn.quasar.dev/logo-v2/svg/logo.svg"
          />
        </q-avatar>
      </template>
    </q-file>

    <q-file filled bottom-slots v-model="model" label="Label" counter>
      <template #prepend>
        <q-icon name="cloud_upload" @click.stop.prevent />
      </template>
      <template #append>
        <q-icon
          name="close"
          @click.stop.prevent="model = null"
          class="cursor-pointer"
        />
      </template>

      <template #hint> Field hint </template>
    </q-file>

    <q-file
      rounded
      outlined
      bottom-slots
      v-model="model"
      label="Label"
      counter
      max-files="12"
    >
      <template #before>
        <q-icon name="attachment" />
      </template>

      <template #append>
        <q-icon
          v-if="model !== null"
          name="close"
          @click.stop.prevent="model = null"
          class="cursor-pointer"
        />
        <q-icon name="search" @click.stop.prevent />
      </template>

      <template #hint> Field hint </template>
    </q-file>

    <q-file
      filled
      bottom-slots
      v-model="model"
      label="Label"
      counter
      max-files="12"
    >
      <template #before>
        <q-avatar>
          <img
            alt="User avatar"
            src="https://cdn.quasar.dev/img/avatar5.jpg"
          />
        </q-avatar>
      </template>

      <template #append>
        <q-icon
          v-if="model !== null"
          name="close"
          @click.stop.prevent="model = null"
          class="cursor-pointer"
        />
        <q-icon name="create_new_folder" @click.stop.prevent />
      </template>

      <template #hint> Field hint </template>

      <template #after>
        <q-btn round dense flat icon="send" />
      </template>
    </q-file>

    <q-file
      filled
      bottom-slots
      v-model="model"
      label="Label"
      counter
      max-files="12"
    >
      <template #before>
        <q-icon name="folder_open" />
      </template>

      <template #hint> Field hint </template>

      <template #append>
        <q-btn round dense flat icon="add" @click.stop.prevent />
      </template>
    </q-file>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const model = ref(null)
</script>
```

### Coloring

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-file color="purple-12" v-model="model" label="Label">
      <template #prepend>
        <q-icon name="attach_file" />
      </template>
    </q-file>

    <q-file color="teal" filled v-model="model" label="Label">
      <template #prepend>
        <q-icon name="cloud_upload" />
      </template>
    </q-file>

    <q-file
      color="grey-3"
      outlined
      label-color="orange"
      v-model="model"
      label="Label"
    >
      <template #append>
        <q-icon name="attachment" color="orange" />
      </template>
    </q-file>

    <q-file
      color="lime-11"
      bg-color="green"
      filled
      v-model="model"
      label="Label"
    >
      <template #prepend>
        <q-icon name="attachment" />
      </template>
    </q-file>

    <q-file color="teal" outlined v-model="model" label="Label">
      <template #append>
        <q-avatar>
          <img
            alt="Quasar logo"
            src="https://cdn.quasar.dev/logo-v2/svg/logo.svg"
          />
        </q-avatar>
      </template>
    </q-file>

    <q-file
      clearable
      color="orange"
      standout
      bottom-slots
      v-model="model"
      label="Label"
      counter
    >
      <template #prepend>
        <q-icon name="attach_file" />
      </template>
      <template #append>
        <q-icon name="favorite" />
      </template>

      <template #hint> Field hint </template>
    </q-file>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const model = ref(null)
</script>
```

### Clearable

As a helper, you can use `clearable` prop so user can reset model to `null` through an appended icon. The second QFile in the example below is the equivalent of using `clearable`.

```vue
<template>
  <div class="q-gutter-y-md column" style="max-width: 300px">
    <q-file
      clearable
      filled
      color="purple-12"
      v-model="model"
      label="Label"
    />

    <!-- equivalent -->
    <q-file color="orange" filled v-model="model" label="Label">
      <template v-if="model" #append>
        <q-icon
          name="cancel"
          @click.stop.prevent="model = null"
          class="cursor-pointer"
        />
      </template>
    </q-file>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const model = ref(null)
</script>
```

### Disable and readonly

```vue
<template>
  <div class="q-gutter-md row">
    <q-file
      disable
      filled
      v-model="model"
      hint="Disable"
      style="width: 250px"
    />

    <q-file
      readonly
      filled
      v-model="model"
      hint="Readonly"
      style="width: 250px"
    />

    <q-file
      disable
      readonly
      filled
      v-model="model"
      hint="Disable and readonly"
      style="width: 250px"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const model = ref(null)
</script>
```

## Usage

> [!WARNING]
> Under the hood, QFile uses a native input. Due to browser security policy, it is not allowed to programmatically fill such an input with a value. As a result, even if you set v-model from the beginning to a value, the component will show those file(s) but the input tag itself won't be filled in with that value. A user interaction (click/tap/<kbd>Enter</kbd> key/<kbd>Space</kbd> key) is absolutely required in order for the native input to contain them. It's best to always have the initial value of model set to `null` or `undefined/void 0`.

### Basic

Example "Single file":

```vue
<template>
  <q-file
    v-model="file"
    label="Pick one file"
    filled
    style="max-width: 300px"
  />
</template>

<script setup>
import { ref } from 'vue'

const file = ref(null)
</script>
```

Example "Multiple files":

```vue
<template>
  <q-file
    v-model="files"
    label="Pick files"
    filled
    multiple
    style="max-width: 300px"
  />
</template>

<script setup>
import { ref } from 'vue'

const files = ref(null)
</script>
```

### Appending files

By default, QFile replaces the model each time the user selects any files through the popup. However, when you are accepting multiple files (`multiple` prop) you can change this behavior and append the new selection to the model rather than replacing its old value.

Below you can pick files multiple times and QFile will keep on appending them to the model:

```vue
<template>
  <q-file
    v-model="files"
    label="Pick files"
    filled
    multiple
    append
    style="max-width: 300px"
  />
</template>

<script setup>
import { ref } from 'vue'

const files = ref(null)
</script>
```

### Counters

Example "Basic counter":

```vue
<template>
  <div class="q-gutter-md row items-start">
    <q-file
      v-model="files"
      label="Pick files"
      filled
      counter
      multiple
      style="max-width: 300px"
    />

    <q-file
      v-model="files"
      label="Pick files"
      filled
      counter
      max-files="3"
      multiple
      style="max-width: 300px"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const files = ref(null)
</script>
```

Example "Counter label":

```vue
<template>
  <div class="q-gutter-md row items-start">
    <q-file
      v-model="files"
      label="Pick files"
      filled
      counter
      :counter-label="counterLabelFn"
      max-files="3"
      multiple
      style="max-width: 300px"
    >
      <template #prepend>
        <q-icon name="attach_file" />
      </template>
    </q-file>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const files = ref(null)

function counterLabelFn({ totalSize, filesNumber, maxFiles }) {
  return `${filesNumber} files of ${maxFiles} | ${totalSize}`
}
</script>
```

### Using chips

Example "With chips":

```vue
<template>
  <q-file
    v-model="files"
    label="Pick files"
    outlined
    use-chips
    multiple
    style="max-width: 300px"
  />
</template>

<script setup>
import { ref } from 'vue'

const files = ref(null)
</script>
```

### Using file slot

The example below highlights how you can customize the display of each file and even incorporate a possible upload progress indicator:

Example "With progress indicator":

```vue
<template>
  <div class="column items-start q-gutter-y-md">
    <q-file
      :model-value="files"
      @update:model-value="updateFiles"
      label="Pick files"
      outlined
      multiple
      :clearable="!isUploading"
      style="max-width: 400px"
    >
      <template #file="{ index, file }">
        <q-chip
          class="full-width q-my-xs"
          :removable="isUploading && uploadProgress[index].percent < 1"
          square
          @remove="cancelFile(index)"
        >
          <q-linear-progress
            class="absolute-full full-height"
            :value="uploadProgress[index].percent"
            :color="uploadProgress[index].color"
            track-color="grey-2"
          />

          <q-avatar>
            <q-icon :name="uploadProgress[index].icon" />
          </q-avatar>

          <div class="ellipsis relative-position">
            {{ file.name }}
          </div>

          <q-tooltip>
            {{ file.name }}
          </q-tooltip>
        </q-chip>
      </template>

      <template #after v-if="canUpload">
        <q-btn
          color="primary"
          dense
          icon="cloud_upload"
          round
          @click="upload"
          :disable="!canUpload"
          :loading="isUploading"
        />
      </template>
    </q-file>
  </div>
</template>

<script setup>
import { computed, onBeforeUnmount, ref } from 'vue'

const files = ref(null)
const uploadProgress = ref([])
const uploading = ref(null)

const isUploading = computed(() => uploading.value !== null)
const canUpload = computed(() => files.value !== null)

function cleanUp() {
  clearTimeout(uploading.value)
}

function updateUploadProgress() {
  let done = true

  uploadProgress.value = uploadProgress.value.map(progress => {
    if (progress.percent === 1 || progress.error) {
      return progress
    }

    const percent = Math.min(1, progress.percent + Math.random() / 10)
    const error = percent < 1 && Math.random() > 0.95

    if (!error && percent < 1 && done) {
      done = false
    }

    return {
      ...progress,
      error,
      color: error ? 'red-2' : 'green-2',
      percent
    }
  })

  uploading.value = done !== true ? setTimeout(updateUploadProgress, 300) : null
}

onBeforeUnmount(cleanUp)

function cancelFile(index) {
  uploadProgress.value[index] = {
    ...uploadProgress.value[index],
    error: true,
    color: 'orange-2'
  }
}

function updateFiles(newFiles) {
  files.value = newFiles
  uploadProgress.value = (newFiles || []).map(file => ({
    error: false,
    color: 'green-2',
    percent: 0,
    icon:
      file.type.indexOf('video/') === 0
        ? 'movie'
        : file.type.indexOf('image/') === 0
          ? 'photo'
          : file.type.indexOf('audio/') === 0
            ? 'audiotrack'
            : 'insert_drive_file'
  }))
}

function upload() {
  cleanUp()

  const allDone = uploadProgress.value.every(progress => progress.percent === 1)

  uploadProgress.value = uploadProgress.value.map(progress => ({
    ...progress,
    error: false,
    color: 'green-2',
    percent: allDone ? 0 : progress.percent
  }))

  updateUploadProgress()
}
</script>
```

### Restricting files

Example "Basic restrictions":

```vue
<template>
  <div class="q-gutter-md row items-start">
    <q-file
      style="max-width: 300px"
      v-model="filesImages"
      filled
      rounded
      label="Restricted to images"
      multiple
      accept=".jpg, image/*"
      @rejected="onRejected"
    />

    <q-file
      style="max-width: 300px"
      v-model="filesMaxSize"
      outlined
      label="Max file size (2k)"
      multiple
      max-file-size="2048"
      @rejected="onRejected"
    />

    <q-file
      style="max-width: 300px"
      v-model="filesMaxTotalSize"
      standout
      label="Max total upload size (4k)"
      multiple
      max-total-size="4096"
      @rejected="onRejected"
    />

    <q-file
      style="max-width: 300px"
      v-model="filesMaxNumber"
      standout
      label="Max number of files (3)"
      multiple
      max-files="3"
      @rejected="onRejected"
    />
  </div>
</template>

<script setup>
import { useQuasar } from 'quasar'
import { ref } from 'vue'

const $q = useQuasar()

const filesImages = ref(null)
const filesMaxSize = ref(null)
const filesMaxTotalSize = ref(null)
const filesMaxNumber = ref(null)

function onRejected(rejectedEntries) {
  // Notify plugin needs to be installed
  // https://v2.quasar.dev/quasar-plugins/notify#Installation
  $q.notify({
    type: 'negative',
    message: `${rejectedEntries.length} file(s) did not pass validation constraints`
  })
}
</script>
```

You can even combine the restrictions above.

> [!NOTE]
> In the example above, we're using `accept` property. Its value must be a comma separated list of unique file type specifiers. Maps to 'accept' attribute of native input type=file element. [More info](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers).

> [!WARNING]
> Recommended format for the `accept` property is `<mediatype>/<extension>`. Examples: "image/png", "image/png". QFile uses an `<input type="file">` under the hood and it relies entirely on the host browser to trigger the file picker. If the `accept` property (that gets applied to the input) is not correct, no file picker will appear on screen or it will appear but it will accept all file types.

You can also apply custom filters (which are executed after user picks files):

Example "Filter":

```vue
<template>
  <div class="q-gutter-md row items-start">
    <q-file
      style="max-width: 300px"
      v-model="filesMaxSize"
      filled
      label="Filtered (for <2k size)"
      multiple
      :filter="checkFileSize"
      @rejected="onRejected"
    />

    <q-file
      style="max-width: 300px"
      v-model="filesPng"
      rounded
      outlined
      label="Filtered (png only)"
      multiple
      :filter="checkFileType"
      @rejected="onRejected"
    />
  </div>
</template>

<script setup>
import { useQuasar } from 'quasar'
import { ref } from 'vue'

const $q = useQuasar()

const filesMaxSize = ref(null)
const filesPng = ref(null)

function checkFileSize(files) {
  return files.filter(file => file.size < 2048)
}

function checkFileType(files) {
  return files.filter(file => file.type === 'image/png')
}

function onRejected(rejectedEntries) {
  // Notify plugin needs to be installed
  // https://v2.quasar.dev/quasar-plugins/notify#Installation
  $q.notify({
    type: 'negative',
    message: `${rejectedEntries.length} file(s) did not pass validation constraints`
  })
}
</script>
```

### Native form submit

When dealing with a native form which has an `action` and a `method` (eg. when using Quasar with ASP.NET controllers), you need to specify the `name` property on QFile, otherwise formData will not contain it (if it should):

Example "Native form":

```vue
<template>
  <q-form @submit="onSubmit" class="q-gutter-md">
    <q-file
      name="poster_file"
      v-model="file"
      filled
      label="Select poster image"
    />

    <q-file
      name="cover_files"
      v-model="files"
      filled
      multiple
      use-chips
      label="Select cover images"
    />

    <div>
      <q-btn label="Submit" type="submit" color="primary" />
    </div>
  </q-form>

  <q-card
    v-if="submitted"
    flat
    bordered
    class="q-mt-md"
    :class="$q.dark.isActive ? 'bg-grey-9' : 'bg-grey-2'"
  >
    <template v-if="submitEmpty">
      <q-card-section>
        Submitted form contains empty formData.
      </q-card-section>
    </template>
    <template v-else>
      <q-card-section
        >Submitted form contains the following formData (key =
        value):</q-card-section
      >
      <q-separator />
      <q-card-section class="row q-gutter-sm items-center">
        <div
          v-for="(item, index) in submitResult"
          :key="index"
          class="q-px-sm q-py-xs bg-grey-8 text-white rounded-borders text-center text-no-wrap"
          >{{ item.name }} = {{ item.value }}</div
        >
      </q-card-section>
    </template>
  </q-card>
</template>

<script setup>
import { ref } from 'vue'

const submitted = ref(false)
const submitEmpty = ref(false)
const submitResult = ref([])

const file = ref(null)
const files = ref(null)

function onSubmit(evt) {
  const formData = new FormData(evt.target)
  const data = []

  for (const [name, value] of formData.entries()) {
    if (value.name.length !== 0) {
      data.push({
        name,
        value: value.name
      })
    }
  }

  submitted.value = true
  submitResult.value = data
  submitEmpty.value = data.length === 0
}
</script>
```

## Accessibility *(v2.25+)*

QFile is built on the QField frame, so label it like any other field: the label association through a generated SSR-safe id, the `role="alert"` error announcements and the keyboard-operable clear button all work as described in [QField's Accessibility section](field.md#accessibility). The validation attributes (`aria-invalid`, `aria-errormessage`, `aria-describedby`) are applied to the underlying native file input.

With the field focused, <kbd>Enter</kbd> or <kbd>Space</kbd> opens the file picker (a `readonly` field still takes focus and shows it, but neither key opens the picker). When `use-chips` is set, each selected file's chip is keyboard-removable through <kbd>Enter</kbd> or <kbd>Space</kbd>, with a localized "Remove" label from the [Quasar Language Pack](../options/quasar-language-packs.md). Drag-and-drop is a pointer-only convenience — the file picker itself is the keyboard path to selecting files.
