Components
File upload

File Upload

A component that allows users to upload files.

Usage

    import { Trash2Icon } from 'lucide-react'
    import { Button } from '~/components/ui/button'
    import * as FileUpload from '~/components/ui/file-upload'
    import { IconButton } from '~/components/ui/icon-button'
    
    export const Demo = (props: FileUpload.RootProps) => {
      return (
        <FileUpload.Root maxFiles={3} {...props}>
          <FileUpload.Dropzone>
            <FileUpload.Label>Drop your files here</FileUpload.Label>
            <FileUpload.Trigger asChild>
              <Button size="sm">Open Dialog</Button>
            </FileUpload.Trigger>
          </FileUpload.Dropzone>
          <FileUpload.ItemGroup>
            {(files) =>
              files.map((file, id) => (
                <FileUpload.Item key={id} file={file}>
                  <FileUpload.ItemPreview type="image/*">
                    <FileUpload.ItemPreviewImage />
                  </FileUpload.ItemPreview>
                  <FileUpload.ItemName />
                  <FileUpload.ItemSizeText />
                  <FileUpload.ItemDeleteTrigger asChild>
                    <IconButton variant="link" size="sm">
                      <Trash2Icon />
                    </IconButton>
                  </FileUpload.ItemDeleteTrigger>
                </FileUpload.Item>
              ))
            }
          </FileUpload.ItemGroup>
        </FileUpload.Root>
      )
    }
    

    Installation

    1

    Add Component

    Insert code snippet into your project. Update import paths as needed.

    import { FileUpload } from '@ark-ui/react/file-upload'
    import type { ComponentProps } from 'react'
    import { styled } from 'styled-system/jsx'
    import { fileUpload } from 'styled-system/recipes'
    import { createStyleContext } from '~/lib/create-style-context'
    
    const { withProvider, withContext } = createStyleContext(fileUpload)
    
    export const Root = withProvider(styled(FileUpload.Root), 'root')
    export const Dropzone = withContext(styled(FileUpload.Dropzone), 'dropzone')
    export const Item = withContext(styled(FileUpload.Item), 'item')
    export const ItemDeleteTrigger = withContext(
      styled(FileUpload.ItemDeleteTrigger),
      'itemDeleteTrigger',
    )
    export const ItemGroup = withContext(styled(FileUpload.ItemGroup), 'itemGroup')
    export const ItemName = withContext(styled(FileUpload.ItemName), 'itemName')
    export const ItemPreview = withContext(styled(FileUpload.ItemPreview), 'itemPreview')
    export const ItemPreviewImage = withContext(styled(FileUpload.ItemPreviewImage), 'itemPreviewImage')
    export const ItemSizeText = withContext(styled(FileUpload.ItemSizeText), 'itemSizeText')
    export const Label = withContext(styled(FileUpload.Label), 'label')
    export const Trigger = withContext(styled(FileUpload.Trigger), 'trigger')
    
    export interface RootProps extends ComponentProps<typeof Root> {}
    export interface DropzoneProps extends ComponentProps<typeof Dropzone> {}
    export interface ItemProps extends ComponentProps<typeof Item> {}
    export interface ItemDeleteTriggerProps extends ComponentProps<typeof ItemDeleteTrigger> {}
    export interface ItemGroupProps extends ComponentProps<typeof ItemGroup> {}
    export interface ItemNameProps extends ComponentProps<typeof ItemName> {}
    export interface ItemPreviewProps extends ComponentProps<typeof ItemPreview> {}
    export interface ItemPreviewImageProps extends ComponentProps<typeof ItemPreviewImage> {}
    export interface ItemSizeTextProps extends ComponentProps<typeof ItemSizeText> {}
    export interface LabelProps extends ComponentProps<typeof Label> {}
    export interface TriggerProps extends ComponentProps<typeof Trigger> {}
    
    2

    Add Recipe

    This step is necessary only if you do not use any of the Park UI plugins.

    import { fileUploadAnatomy } from '@ark-ui/anatomy'
    import { defineSlotRecipe } from '@pandacss/dev'
    
    export const fileUpload = defineSlotRecipe({
      className: 'fileUpload',
      slots: fileUploadAnatomy.keys(),
      base: {
        root: {
          display: 'flex',
          flexDirection: 'column',
          gap: '4',
          width: '100%',
        },
        label: {
          fontWeight: 'medium',
          textStyle: 'sm',
        },
        dropzone: {
          alignItems: 'center',
          background: 'bg.default',
          borderRadius: 'l3',
          borderWidth: '1px',
          display: 'flex',
          flexDirection: 'column',
          gap: '3',
          justifyContent: 'center',
          minHeight: 'xs',
          px: '6',
          py: '4',
        },
        item: {
          animation: 'fadeIn 0.25s ease-out',
          background: 'bg.default',
          borderRadius: 'l3',
          borderWidth: '1px',
          columnGap: '3',
          display: 'grid',
          gridTemplateColumns: 'auto 1fr auto',
          gridTemplateAreas: `
            "preview name delete"
            "preview size delete"
            `,
          p: '4',
        },
        itemGroup: {
          display: 'flex',
          flexDirection: 'column',
          gap: '3',
        },
        itemName: {
          color: 'fg.default',
          fontWeight: 'medium',
          gridArea: 'name',
          textStyle: 'sm',
        },
        itemSizeText: {
          color: 'fg.muted',
          gridArea: 'size',
          textStyle: 'sm',
        },
        itemDeleteTrigger: {
          alignSelf: 'start',
          gridArea: 'delete',
        },
        itemPreview: {
          gridArea: 'preview',
        },
        itemPreviewImage: {
          aspectRatio: '1',
          height: '10',
          objectFit: 'scale-down',
          width: '10',
        },
      },
    })
    

    On this page