FileUpload

GitHub

Usage

Examples

API

Props

Prop Default Type
as

'div'

any

The element or component this component should render as.

id

string

name

string

icon

appConfig.ui.icons.upload

string

The icon to display.

label

string

description

string

color

'primary'

"error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"

variant

'area'

"button" | "area"

The button variant is only available when multiple is false.

size

'md'

"xs" | "sm" | "md" | "lg" | "xl"

layout

'list'

"list" | "grid"

The layout of how files are displayed. Only works when variant is area.

position

'outside'

"inside" | "outside"

The position of the files. Only works when variant is area and when layout is list.

highlight

boolean

Highlight the ring color like a focus state.

accept

'*'

string

Specifies the allowed file types for the input. Provide a comma-separated list of MIME types or file extensions (e.g., "image/png,application/pdf,.jpg").

multiple

false

boolean

reset

false

boolean

Reset the file input when the dialog is opened.

dropzone

true

boolean

Create a zone that allows the user to drop files onto it.

interactive

true

boolean

Make the dropzone interactive when the user is clicking on it.

required

boolean

disabled

boolean

fileIcon

appConfig.ui.icons.file

string

The icon to display for the file.

fileDelete

boolean | Partial<ButtonProps>

Configure the delete button for the file. When layout is grid, the default is { color: 'neutral', variant: 'solid', size: 'xs' } When layout is list, the default is { color: 'neutral', variant: 'link' }

fileDeleteIcon

appConfig.ui.icons.close

string

The icon displayed to delete a file.

modelValue

null | File | File[]

ui

{ root?: ClassNameValue; base?: ClassNameValue; wrapper?: ClassNameValue; icon?: ClassNameValue; avatar?: ClassNameValue; label?: ClassNameValue; description?: ClassNameValue; actions?: ClassNameValue; files?: ClassNameValue; file?: ClassNameValue; fileLeadingAvatar?: ClassNameValue; fileWrapper?: ClassNameValue; fileName?: ClassNameValue; fileSize?: ClassNameValue; fileTrailingButton?: ClassNameValue; }

Slots

Slot Type
default

{ open: (localOptions?: Partial<UseFileDialogOptions> | undefined) => void; removeFile: (index?: number | undefined) => void; }

leading

{}

label

{}

description

{}

actions

{ files?: FileUploadFiles<boolean> | undefined; open: (localOptions?: Partial<UseFileDialogOptions> | undefined) => void; removeFile: (index?: number | undefined) => void; }

files

{ files?: FileUploadFiles<boolean> | undefined; }

files-top

{ files?: FileUploadFiles<boolean> | undefined; open: (localOptions?: Partial<UseFileDialogOptions> | undefined) => void; removeFile: (index?: number | undefined) => void; }

files-bottom

{ files?: FileUploadFiles<boolean> | undefined; open: (localOptions?: Partial<UseFileDialogOptions> | undefined) => void; removeFile: (index?: number | undefined) => void; }

file

{ file: File; index: number; }

file-leading

{ file: File; index: number; }

file-name

{ file: File; index: number; }

file-size

{ file: File; index: number; }

file-trailing

{ file: File; index: number; }

Emits

Event Type
change

[event: Event]

update:modelValue

[payload: File | File[] | null]

update:modelValue

[value: File | File[] | null | undefined]

Expose

When accessing the component via a template ref, you can use the following:

NameType
inputRefRef<HTMLInputElement | null>
dropzoneRefRef<HTMLDivElement | null>

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    fileUpload: {
      slots: {
        root: 'relative',
        base: [
          'w-full bg-default border border-default flex flex-col gap-2 items-stretch justify-center rounded-lg focus-visible:outline-2',
          'transition-[background]'
        ],
        wrapper: 'flex flex-col items-center justify-center text-center',
        icon: 'shrink-0',
        avatar: 'shrink-0',
        label: 'font-medium text-default mt-2',
        description: 'text-muted mt-1',
        actions: 'flex flex-wrap gap-1.5 shrink-0 mt-4',
        files: '',
        file: 'relative',
        fileLeadingAvatar: 'shrink-0',
        fileWrapper: 'flex flex-col min-w-0',
        fileName: 'text-default truncate',
        fileSize: 'text-muted truncate',
        fileTrailingButton: ''
      },
      variants: {
        color: {
          primary: '',
          secondary: '',
          success: '',
          info: '',
          warning: '',
          error: '',
          neutral: ''
        },
        variant: {
          area: {
            wrapper: 'px-4 py-3',
            base: 'p-4'
          },
          button: {}
        },
        size: {
          xs: {
            base: 'text-xs',
            icon: 'size-4',
            file: 'text-xs px-2 py-1 gap-1',
            fileWrapper: 'flex-row gap-1'
          },
          sm: {
            base: 'text-xs',
            icon: 'size-4',
            file: 'text-xs px-2.5 py-1.5 gap-1.5',
            fileWrapper: 'flex-row gap-1'
          },
          md: {
            base: 'text-sm',
            icon: 'size-5',
            file: 'text-xs px-2.5 py-1.5 gap-1.5'
          },
          lg: {
            base: 'text-sm',
            icon: 'size-5',
            file: 'text-sm px-3 py-2 gap-2',
            fileSize: 'text-xs'
          },
          xl: {
            base: 'text-base',
            icon: 'size-6',
            file: 'text-sm px-3 py-2 gap-2'
          }
        },
        layout: {
          list: {
            root: 'flex flex-col gap-2 items-start',
            files: 'flex flex-col w-full gap-2',
            file: 'min-w-0 flex items-center border border-default rounded-md w-full',
            fileTrailingButton: 'ms-auto'
          },
          grid: {
            fileWrapper: 'hidden',
            fileLeadingAvatar: 'size-full rounded-lg',
            fileTrailingButton: 'absolute -top-1.5 -right-1.5 p-0 rounded-full border-2 border-bg'
          }
        },
        position: {
          inside: '',
          outside: ''
        },
        dropzone: {
          true: 'border-dashed data-[dragging=true]:bg-elevated/25'
        },
        interactive: {
          true: ''
        },
        highlight: {
          true: ''
        },
        multiple: {
          true: ''
        },
        disabled: {
          true: 'cursor-not-allowed opacity-75'
        }
      },
      compoundVariants: [
        {
          color: 'primary',
          class: 'focus-visible:outline-primary'
        },
        {
          color: 'primary',
          highlight: true,
          class: 'ring ring-inset ring-primary'
        },
        {
          color: 'neutral',
          class: 'focus-visible:outline-inverted'
        },
        {
          color: 'neutral',
          highlight: true,
          class: 'ring ring-inset ring-inverted'
        },
        {
          size: 'xs',
          layout: 'list',
          class: {
            fileTrailingButton: '-mr-1'
          }
        },
        {
          size: 'sm',
          layout: 'list',
          class: {
            fileTrailingButton: '-mr-1.5'
          }
        },
        {
          size: 'md',
          layout: 'list',
          class: {
            fileTrailingButton: '-mr-1.5'
          }
        },
        {
          size: 'lg',
          layout: 'list',
          class: {
            fileTrailingButton: '-mr-2'
          }
        },
        {
          size: 'xl',
          layout: 'list',
          class: {
            fileTrailingButton: '-mr-2'
          }
        },
        {
          variant: 'button',
          size: 'xs',
          class: {
            base: 'p-1'
          }
        },
        {
          variant: 'button',
          size: 'sm',
          class: {
            base: 'p-1.5'
          }
        },
        {
          variant: 'button',
          size: 'md',
          class: {
            base: 'p-1.5'
          }
        },
        {
          variant: 'button',
          size: 'lg',
          class: {
            base: 'p-2'
          }
        },
        {
          variant: 'button',
          size: 'xl',
          class: {
            base: 'p-2'
          }
        },
        {
          layout: 'grid',
          multiple: true,
          class: {
            files: 'grid grid-cols-2 md:grid-cols-3 gap-4 w-full',
            file: 'p-0 aspect-square'
          }
        },
        {
          layout: 'grid',
          multiple: false,
          class: {
            file: 'absolute inset-0 p-0'
          }
        },
        {
          interactive: true,
          disabled: false,
          class: 'hover:bg-elevated/25'
        }
      ],
      defaultVariants: {
        color: 'primary',
        variant: 'area',
        size: 'md'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        fileUpload: {
          slots: {
            root: 'relative',
            base: [
              'w-full bg-default border border-default flex flex-col gap-2 items-stretch justify-center rounded-lg focus-visible:outline-2',
              'transition-[background]'
            ],
            wrapper: 'flex flex-col items-center justify-center text-center',
            icon: 'shrink-0',
            avatar: 'shrink-0',
            label: 'font-medium text-default mt-2',
            description: 'text-muted mt-1',
            actions: 'flex flex-wrap gap-1.5 shrink-0 mt-4',
            files: '',
            file: 'relative',
            fileLeadingAvatar: 'shrink-0',
            fileWrapper: 'flex flex-col min-w-0',
            fileName: 'text-default truncate',
            fileSize: 'text-muted truncate',
            fileTrailingButton: ''
          },
          variants: {
            color: {
              primary: '',
              secondary: '',
              success: '',
              info: '',
              warning: '',
              error: '',
              neutral: ''
            },
            variant: {
              area: {
                wrapper: 'px-4 py-3',
                base: 'p-4'
              },
              button: {}
            },
            size: {
              xs: {
                base: 'text-xs',
                icon: 'size-4',
                file: 'text-xs px-2 py-1 gap-1',
                fileWrapper: 'flex-row gap-1'
              },
              sm: {
                base: 'text-xs',
                icon: 'size-4',
                file: 'text-xs px-2.5 py-1.5 gap-1.5',
                fileWrapper: 'flex-row gap-1'
              },
              md: {
                base: 'text-sm',
                icon: 'size-5',
                file: 'text-xs px-2.5 py-1.5 gap-1.5'
              },
              lg: {
                base: 'text-sm',
                icon: 'size-5',
                file: 'text-sm px-3 py-2 gap-2',
                fileSize: 'text-xs'
              },
              xl: {
                base: 'text-base',
                icon: 'size-6',
                file: 'text-sm px-3 py-2 gap-2'
              }
            },
            layout: {
              list: {
                root: 'flex flex-col gap-2 items-start',
                files: 'flex flex-col w-full gap-2',
                file: 'min-w-0 flex items-center border border-default rounded-md w-full',
                fileTrailingButton: 'ms-auto'
              },
              grid: {
                fileWrapper: 'hidden',
                fileLeadingAvatar: 'size-full rounded-lg',
                fileTrailingButton: 'absolute -top-1.5 -right-1.5 p-0 rounded-full border-2 border-bg'
              }
            },
            position: {
              inside: '',
              outside: ''
            },
            dropzone: {
              true: 'border-dashed data-[dragging=true]:bg-elevated/25'
            },
            interactive: {
              true: ''
            },
            highlight: {
              true: ''
            },
            multiple: {
              true: ''
            },
            disabled: {
              true: 'cursor-not-allowed opacity-75'
            }
          },
          compoundVariants: [
            {
              color: 'primary',
              class: 'focus-visible:outline-primary'
            },
            {
              color: 'primary',
              highlight: true,
              class: 'ring ring-inset ring-primary'
            },
            {
              color: 'neutral',
              class: 'focus-visible:outline-inverted'
            },
            {
              color: 'neutral',
              highlight: true,
              class: 'ring ring-inset ring-inverted'
            },
            {
              size: 'xs',
              layout: 'list',
              class: {
                fileTrailingButton: '-mr-1'
              }
            },
            {
              size: 'sm',
              layout: 'list',
              class: {
                fileTrailingButton: '-mr-1.5'
              }
            },
            {
              size: 'md',
              layout: 'list',
              class: {
                fileTrailingButton: '-mr-1.5'
              }
            },
            {
              size: 'lg',
              layout: 'list',
              class: {
                fileTrailingButton: '-mr-2'
              }
            },
            {
              size: 'xl',
              layout: 'list',
              class: {
                fileTrailingButton: '-mr-2'
              }
            },
            {
              variant: 'button',
              size: 'xs',
              class: {
                base: 'p-1'
              }
            },
            {
              variant: 'button',
              size: 'sm',
              class: {
                base: 'p-1.5'
              }
            },
            {
              variant: 'button',
              size: 'md',
              class: {
                base: 'p-1.5'
              }
            },
            {
              variant: 'button',
              size: 'lg',
              class: {
                base: 'p-2'
              }
            },
            {
              variant: 'button',
              size: 'xl',
              class: {
                base: 'p-2'
              }
            },
            {
              layout: 'grid',
              multiple: true,
              class: {
                files: 'grid grid-cols-2 md:grid-cols-3 gap-4 w-full',
                file: 'p-0 aspect-square'
              }
            },
            {
              layout: 'grid',
              multiple: false,
              class: {
                file: 'absolute inset-0 p-0'
              }
            },
            {
              interactive: true,
              disabled: false,
              class: 'hover:bg-elevated/25'
            }
          ],
          defaultVariants: {
            color: 'primary',
            variant: 'area',
            size: 'md'
          }
        }
      }
    })
  ]
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'

export default defineConfig({
  plugins: [
    vue(),
    uiPro({
      ui: {
        fileUpload: {
          slots: {
            root: 'relative',
            base: [
              'w-full bg-default border border-default flex flex-col gap-2 items-stretch justify-center rounded-lg focus-visible:outline-2',
              'transition-[background]'
            ],
            wrapper: 'flex flex-col items-center justify-center text-center',
            icon: 'shrink-0',
            avatar: 'shrink-0',
            label: 'font-medium text-default mt-2',
            description: 'text-muted mt-1',
            actions: 'flex flex-wrap gap-1.5 shrink-0 mt-4',
            files: '',
            file: 'relative',
            fileLeadingAvatar: 'shrink-0',
            fileWrapper: 'flex flex-col min-w-0',
            fileName: 'text-default truncate',
            fileSize: 'text-muted truncate',
            fileTrailingButton: ''
          },
          variants: {
            color: {
              primary: '',
              secondary: '',
              success: '',
              info: '',
              warning: '',
              error: '',
              neutral: ''
            },
            variant: {
              area: {
                wrapper: 'px-4 py-3',
                base: 'p-4'
              },
              button: {}
            },
            size: {
              xs: {
                base: 'text-xs',
                icon: 'size-4',
                file: 'text-xs px-2 py-1 gap-1',
                fileWrapper: 'flex-row gap-1'
              },
              sm: {
                base: 'text-xs',
                icon: 'size-4',
                file: 'text-xs px-2.5 py-1.5 gap-1.5',
                fileWrapper: 'flex-row gap-1'
              },
              md: {
                base: 'text-sm',
                icon: 'size-5',
                file: 'text-xs px-2.5 py-1.5 gap-1.5'
              },
              lg: {
                base: 'text-sm',
                icon: 'size-5',
                file: 'text-sm px-3 py-2 gap-2',
                fileSize: 'text-xs'
              },
              xl: {
                base: 'text-base',
                icon: 'size-6',
                file: 'text-sm px-3 py-2 gap-2'
              }
            },
            layout: {
              list: {
                root: 'flex flex-col gap-2 items-start',
                files: 'flex flex-col w-full gap-2',
                file: 'min-w-0 flex items-center border border-default rounded-md w-full',
                fileTrailingButton: 'ms-auto'
              },
              grid: {
                fileWrapper: 'hidden',
                fileLeadingAvatar: 'size-full rounded-lg',
                fileTrailingButton: 'absolute -top-1.5 -right-1.5 p-0 rounded-full border-2 border-bg'
              }
            },
            position: {
              inside: '',
              outside: ''
            },
            dropzone: {
              true: 'border-dashed data-[dragging=true]:bg-elevated/25'
            },
            interactive: {
              true: ''
            },
            highlight: {
              true: ''
            },
            multiple: {
              true: ''
            },
            disabled: {
              true: 'cursor-not-allowed opacity-75'
            }
          },
          compoundVariants: [
            {
              color: 'primary',
              class: 'focus-visible:outline-primary'
            },
            {
              color: 'primary',
              highlight: true,
              class: 'ring ring-inset ring-primary'
            },
            {
              color: 'neutral',
              class: 'focus-visible:outline-inverted'
            },
            {
              color: 'neutral',
              highlight: true,
              class: 'ring ring-inset ring-inverted'
            },
            {
              size: 'xs',
              layout: 'list',
              class: {
                fileTrailingButton: '-mr-1'
              }
            },
            {
              size: 'sm',
              layout: 'list',
              class: {
                fileTrailingButton: '-mr-1.5'
              }
            },
            {
              size: 'md',
              layout: 'list',
              class: {
                fileTrailingButton: '-mr-1.5'
              }
            },
            {
              size: 'lg',
              layout: 'list',
              class: {
                fileTrailingButton: '-mr-2'
              }
            },
            {
              size: 'xl',
              layout: 'list',
              class: {
                fileTrailingButton: '-mr-2'
              }
            },
            {
              variant: 'button',
              size: 'xs',
              class: {
                base: 'p-1'
              }
            },
            {
              variant: 'button',
              size: 'sm',
              class: {
                base: 'p-1.5'
              }
            },
            {
              variant: 'button',
              size: 'md',
              class: {
                base: 'p-1.5'
              }
            },
            {
              variant: 'button',
              size: 'lg',
              class: {
                base: 'p-2'
              }
            },
            {
              variant: 'button',
              size: 'xl',
              class: {
                base: 'p-2'
              }
            },
            {
              layout: 'grid',
              multiple: true,
              class: {
                files: 'grid grid-cols-2 md:grid-cols-3 gap-4 w-full',
                file: 'p-0 aspect-square'
              }
            },
            {
              layout: 'grid',
              multiple: false,
              class: {
                file: 'absolute inset-0 p-0'
              }
            },
            {
              interactive: true,
              disabled: false,
              class: 'hover:bg-elevated/25'
            }
          ],
          defaultVariants: {
            color: 'primary',
            variant: 'area',
            size: 'md'
          }
        }
      }
    })
  ]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitHub.