Form File

File input control that supports single and multiple file modes, drag and drop, file type restrictions, and directory selection with contextual state feedback.

Overview

BFormFile provides a customized, cross-browser consistent file input control with support for:

  • Single and multiple file selection
  • Drag and drop file upload
  • Directory selection (browser support required)
  • File type filtering via accept attribute
  • Custom text and placeholders
  • Bootstrap validation states

Single File Mode

The default behavior is single file mode. While using single file mode the modelValue will be a single File object

No file chosen
Files: No file selected
HTML
vue
<template>
  <BFormFile
    v-model="file"
    label="Hello!"
  />
  <div class="mt-3">
    Files: <strong>{{ file?.name || 'No file selected' }}</strong>
  </div>
</template>

<script setup lang="ts">
import {ref} from 'vue'

const file = ref<null | File>(null)
</script>

Multiple File Mode

To toggle multiple file mode, simply set the multiple prop to true. While in multiple file mode, the modelValue will be a File[], even if only one file is selected

No file chosen
Files: No files selected
HTML
vue
<template>
  <BFormFile
    v-model="files"
    multiple
  />
  <div class="mt-3">
    Files: <strong>{{ files?.map((file) => file.name).join(', ') || 'No files selected' }}</strong>
  </div>
</template>

<script setup lang="ts">
import {ref} from 'vue'

const files = ref<null | File[]>(null)
</script>

Limiting to certain file types

You can limit the file types by setting the accept prop. The accept attribute is a csv list of acceptable types. This can be a string or string[]. If a string[] is inputted, it simply gets joined as a csv list

No file chosen
Files: No file selected
HTML
vue
<template>
  <BFormFile
    v-model="file"
    accept="image/*"
  />
  <div class="mt-3">
    Files: <strong>{{ file?.name || 'No file selected' }}</strong>
  </div>
</template>

<script setup lang="ts">
import {ref} from 'vue'

const file = ref<null | File>(null)
</script>

Drag and Drop Support

Drag and drop support uses the browsers default behavior. You can explicitly disable drag and drop by using the noDrop prop

No file chosen
Files: No file selected
HTML
vue
<template>
  <BFormFile
    v-model="file"
    no-drop
  />
  <div class="mt-3">
    Files: <strong>{{ file?.name || 'No file selected' }}</strong>
  </div>
</template>

<script setup lang="ts">
import {ref} from 'vue'

const file = ref<null | File>(null)
</script>

Sizing

You can modify the size of the form control by using the size prop

No file chosen
No file chosen
No file chosen
HTML
template
<template>
  <BFormFile
    class="mt-3"
    size="sm"
  />
  <BFormFile class="mt-3" />
  <BFormFile
    class="mt-3"
    size="lg"
  />
</template>

Label

You can add a label above the input by using the label prop or the label slot

No file chosen
No file chosen
HTML
template
<template>
  <BFormFile
    class="mt-3"
    label="I am first!"
  />
  <BFormFile class="mt-3">
    <template #label> I am second! </template>
  </BFormFile>
</template>

Customizing Text and Placeholders

Browse Button Text

Customize the browse button text using the browseText prop (custom mode only):

No files selected
Selected: None

Select a document
Selected: None
HTML
vue
<template>
  <div>
    <BFormFile
      v-model="file1"
      browse-text="Choose File"
      placeholder="No files selected"
    />
    <div class="mt-3">
      Selected: <strong>{{ file1?.name || 'None' }}</strong>
    </div>

    <hr />

    <BFormFile
      v-model="file2"
      browse-text="Pick a File"
      placeholder="Select a document"
      class="mt-3"
    />
    <div class="mt-3">
      Selected: <strong>{{ file2?.name || 'None' }}</strong>
    </div>
  </div>
</template>

<script setup lang="ts">
import {ref} from 'vue'

const file1 = ref<File | null>(null)
const file2 = ref<File | null>(null)
</script>

Drop Zone Placeholders

Customize the placeholder text shown in different states (custom mode only):

Choose files or drag them here
Selected: 0 files
HTML
vue
<template>
  <div>
    <BFormFile
      v-model="files"
      multiple
      drop-placeholder="Drop files here..."
      no-drop-placeholder="Drag and drop is disabled"
      placeholder="Choose files or drag them here"
    />
    <div class="mt-3">Selected: {{ files?.length || 0 }} files</div>
  </div>
</template>

<script setup lang="ts">
import {ref} from 'vue'

const files = ref<File[] | null>(null)
</script>

File Name Formatting

Use the fileNameFormatter prop to customize how selected file names are displayed (custom mode only):

Choose multiple files
Display: None
HTML
vue
<template>
  <div>
    <BFormFile
      v-model="files"
      multiple
      :file-name-formatter="formatNames"
      placeholder="Choose multiple files"
    />
    <div class="mt-3">Display: {{ displayText }}</div>
  </div>
</template>

<script setup lang="ts">
import {computed, ref} from 'vue'

const files = ref<File[] | null>(null)

const formatNames = (files: readonly File[]): string => {
  if (files.length === 1) {
    return files[0].name
  }
  return `${files.length} files selected (${files.map((f) => f.name).join(', ')})`
}

const displayText = computed(() => {
  if (!files.value || files.value.length === 0) return 'None'
  return formatNames(files.value)
})
</script>

Plain Mode

Use the plain prop to render a native HTML file input without custom styling. This provides 100% backward compatibility with the original implementation:

Selected: 0 files
HTML
vue
<template>
  <div>
    <BFormFile
      v-model="files"
      multiple
      plain
      placeholder="Choose files"
    />
    <div class="mt-3">Selected: {{ files?.length || 0 }} files</div>
  </div>
</template>

<script setup lang="ts">
import {ref} from 'vue'

const files = ref<File[] | null>(null)
</script>

Directory Mode

By adding the directory prop, a user can select directories instead of files.

Select a directory
No directory selected
HTML
vue
<template>
  <div>
    <BFormFile
      v-model="files"
      directory
      multiple
      placeholder="Select a directory"
      browse-text="Browse Directories"
    />
    <div class="mt-3">
      <div v-if="files && files.length > 0">
        <strong>{{ files.length }} files selected from directory:</strong>
        <ul class="mt-2 font-monospace small">
          <li
            v-for="(file, index) in files.slice(0, 10)"
            :key="index"
          >
            {{ file.webkitRelativePath || file.name }}
          </li>
          <li v-if="files.length > 10">
            <em>...and {{ files.length - 10 }} more files</em>
          </li>
        </ul>
        <p class="text-muted small mt-2">
          Each file includes a <code>webkitRelativePath</code> property with its relative path.
        </p>
      </div>
      <div v-else>No directory selected</div>
    </div>
  </div>
</template>

<script setup lang="ts">
import {ref} from 'vue'

const files = ref<File[] | null>(null)
</script>

Accessing File Paths

When using directory mode, each File object includes the standard webkitRelativePath property containing the relative path from the selected directory root:

ts
// Example: After selecting a directory with BFormFile
const files: File[] = [] // Your selected files from v-model

files.forEach((file: File) => {
  console.log(file.name) // "helpers.ts"
  console.log(file.webkitRelativePath) // "src/utils/helpers.ts"
})

The webkitRelativePath property allows you to:

  • Display the full file path to users
  • Reconstruct directory structure in your application
  • Group files by folder
  • Preserve directory hierarchy when processing files

Browser Compatibility

The webkitRelativePath property is available in all browsers that support directory selection. It's part of the standard File API when using the webkitdirectory attribute.

Autofocus

If you set the autofocus prop to true, the input will be focused when the component is inserted

No file chosen
HTML
template
<template>
  <BFormFile
    class="mt-3"
    autofocus
  />
</template>

Contextual State

You can use the state prop to provide visual feedback on the state of the input

No file chosen
No file chosen
HTML
template
<template>
  <BFormFile
    class="mt-3"
    :state="false"
  />
  <BFormFile
    class="mt-3"
    :state="true"
  />
</template>

Important Notes

Prop Reactivity

The accept, multiple, and directory props support runtime changes. The file dialog and drop zone will automatically reflect updated values when these props change.

Drop Zone Multiple Limitation

The drop zone's multiple validation is set at component initialization and will not update if the multiple prop changes at runtime. However, the actual file handling logic respects the current multiple prop value, so files will be processed correctly. If you need the drop zone validation to update, remount the component with a new key attribute when changing the multiple prop.

Modifying the file selection

With inputs that are of type file, the value is strictly uni-directional. Meaning that you cannot change the value of the input via JavaScript. You can change the value of the v-model, and this will work for an "outside view", however, the actual input element will not have its FileList changed. This is for security reasons as a malicious script could attempt to read and steal documents

Exposed functions

The BFormFile exposes functions to control the component: focus(), blur(), reset(). These are accessed through the template ref.

  1. focus(): Focuses the file input (or browse button in custom mode)
  2. blur(): Blurs the file input focus
  3. reset(): Resets the file selection so that no file is selected

Component Reference

<BFormFile>

PropTypeDefaultDescription
acceptstring | readonly string[]'' Value to set on the file input's `accept` attribute. Restricts file types that can be selected
aria-labelstringundefined Sets the value of `aria-label` attribute on the rendered element
aria-labelledbystringundefined The ID of the element that provides a label for this component. Used as the value for the `aria-labelledby` attribute
autofocusbooleanfalse When set to `true`, attempts to auto-focus the control when it is mounted, or re-activated when in a keep-alive. Does not set the `autofocus` attribute on the control
browse-textstring'Browse' Text for the browse button (custom mode only)
capture'user' | 'environment'undefined When set, will instruct the browser to use the device's camera (if supported). 'user' requests the user-facing camera, 'environment' requests the outward-facing camera
directorybooleanfalse Enable `directory` mode (on browsers that support it)
disabledbooleanfalse When set to `true`, disables the component's functionality and places it in a disabled state
drop-placeholderstringundefined Text to display when dragging files over the drop zone (custom mode only). Defaults to "Drop files here..." when not specified
file-name-formatter(files: readonly File[]) => stringundefined Custom formatter function for displaying selected file names (custom mode only)
formstringundefined ID of the form that the form control belongs to. Sets the `form` attribute on the control
idstringundefined Used to set the `id` attribute on the rendered content, and used as the base to generate any additional element IDs as needed
labelstring'' Sets the label for the form group which the file input is rendered
label-classClassValueundefined Sets the styling for the label
model-valuereadonly File[] | File | nullundefined The current value of the file input. Will be a single `File` object or an array of `File` objects (if `multiple` or `directory` is set). Can be set to `null`, or an empty array to reset the file input
multiplebooleanfalse When set, will allow multiple files to be selected. `v-model` will be an array
namestringundefined Sets the value of the `name` attribute on the form control
no-buttonboolean | nullundefined Hide the file input button
no-dropbooleanfalse Disable drag and drop mode
placeholderstring'No file chosen' Text to display when no file is selected (custom mode only)
plainbooleanfalse Don't add any additional styling or classes to the file input
requiredbooleanundefined Adds the `required` attribute to the form control
show-file-namesbooleanfalse Display selected file names in custom mode
sizeSize'md' Set the size of the component's appearance. 'sm', 'md' (default), or 'lg'
stateValidationStateundefined Controls the validation state appearance of the component. `true` for valid, `false` for invalid, or `null` for no validation state
EventArgsDescription
change
value: Event | CustomEvent - Native Event (plain mode) or CustomEvent with file data (custom mode)
Emitted when the file selection changes. In plain mode, receives the native Event from the file input. In custom mode, receives a CustomEvent with `detail.files` (File[]), `detail.target.files` (FileList-like), and a `files` property for convenience.
update:model-value
value: File | File[] | null - Will be a single File object in single mode or an array of File objects in multiple mode
Updates the `v-model` value (see docs for more details)
NameScopeDescription
drop-placeholderSlot to customize the drag-and-drop overlay text (shown during drag operations)
file-name
files: readonly File[] - Array of selected File objects
names: readonly string[] - Array of file names
Slot to customize how selected file names are displayed
labelSlot to customize the label content
placeholderSlot to customize the placeholder text shown when no files are selected