File Upload
A component that allows users to upload files.
'use client'
import { Trash2Icon } from 'lucide-react'
import { Button } from '~/components/ui/button'
import { 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>
        <FileUpload.Context>
          {({ acceptedFiles }) =>
            acceptedFiles.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.Context>
      </FileUpload.ItemGroup>
      <FileUpload.HiddenInput />
    </FileUpload.Root>
  )
}
import { Trash2Icon } from 'lucide-solid'
import { For } from 'solid-js'
import { Button } from '~/components/ui/button'
import { 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={(triggerProps) => (
            <Button size="sm" {...triggerProps()}>
              Open Dialog
            </Button>
          )}
        />
      </FileUpload.Dropzone>
      <FileUpload.ItemGroup>
        <FileUpload.Context>
          {(fileUpload) => (
            <For each={fileUpload().acceptedFiles}>
              {(file) => (
                <FileUpload.Item file={file}>
                  <FileUpload.ItemPreview type="image/*">
                    <FileUpload.ItemPreviewImage />
                  </FileUpload.ItemPreview>
                  <FileUpload.ItemName />
                  <FileUpload.ItemSizeText />
                  <FileUpload.ItemDeleteTrigger
                    asChild={(triggerProps) => (
                      <IconButton variant="link" size="sm" {...triggerProps()}>
                        <Trash2Icon />
                      </IconButton>
                    )}
                  />
                </FileUpload.Item>
              )}
            </For>
          )}
        </FileUpload.Context>
      </FileUpload.ItemGroup>
      <FileUpload.HiddenInput />
    </FileUpload.Root>
  )
}
Usage
import { FileUpload } from '~/components/ui/file-upload'Installation
npx @park-ui/cli components add file-upload1
Add Styled Primitive
Copy the code snippet below into ~/components/ui/styled/file-upload.tsx
'use client'
import type { Assign } from '@ark-ui/react'
import { FileUpload } from '@ark-ui/react/file-upload'
import { type FileUploadVariantProps, fileUpload } from 'styled-system/recipes'
import type { ComponentProps, HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(fileUpload)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
  HTMLDivElement,
  Assign<Assign<HTMLStyledProps<'div'>, FileUpload.RootProviderBaseProps>, FileUploadVariantProps>
>(FileUpload.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
  HTMLDivElement,
  Assign<Assign<HTMLStyledProps<'div'>, FileUpload.RootBaseProps>, FileUploadVariantProps>
>(FileUpload.Root, 'root')
export const Dropzone = withContext<
  HTMLDivElement,
  Assign<HTMLStyledProps<'div'>, FileUpload.DropzoneBaseProps>
>(FileUpload.Dropzone, 'dropzone')
export const ItemDeleteTrigger = withContext<
  HTMLButtonElement,
  Assign<HTMLStyledProps<'button'>, FileUpload.ItemDeleteTriggerBaseProps>
>(FileUpload.ItemDeleteTrigger, 'itemDeleteTrigger')
export const ItemGroup = withContext<
  HTMLUListElement,
  Assign<HTMLStyledProps<'ul'>, FileUpload.ItemGroupBaseProps>
>(FileUpload.ItemGroup, 'itemGroup')
export const ItemName = withContext<
  HTMLDivElement,
  Assign<HTMLStyledProps<'div'>, FileUpload.ItemNameBaseProps>
>(FileUpload.ItemName, 'itemName')
export const ItemPreviewImage = withContext<
  HTMLImageElement,
  Assign<HTMLStyledProps<'img'>, FileUpload.ItemPreviewImageBaseProps>
>(FileUpload.ItemPreviewImage, 'itemPreviewImage')
export const ItemPreview = withContext<
  HTMLImageElement,
  Assign<HTMLStyledProps<'div'>, FileUpload.ItemPreviewBaseProps>
>(FileUpload.ItemPreview, 'itemPreview')
export const Item = withContext<
  HTMLLIElement,
  Assign<HTMLStyledProps<'li'>, FileUpload.ItemBaseProps>
>(FileUpload.Item, 'item')
export const ItemSizeText = withContext<
  HTMLDivElement,
  Assign<HTMLStyledProps<'div'>, FileUpload.ItemSizeTextBaseProps>
>(FileUpload.ItemSizeText, 'itemSizeText')
export const Label = withContext<
  HTMLLabelElement,
  Assign<HTMLStyledProps<'label'>, FileUpload.LabelBaseProps>
>(FileUpload.Label, 'label')
export const Trigger = withContext<
  HTMLButtonElement,
  Assign<HTMLStyledProps<'button'>, FileUpload.TriggerBaseProps>
>(FileUpload.Trigger, 'trigger')
export {
  FileUploadContext as Context,
  FileUploadHiddenInput as HiddenInput,
} from '@ark-ui/react/file-upload'
import { type Assign, FileUpload } from '@ark-ui/solid'
import type { ComponentProps } from 'solid-js'
import { type FileUploadVariantProps, fileUpload } from 'styled-system/recipes'
import type { HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(fileUpload)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
  Assign<Assign<HTMLStyledProps<'div'>, FileUpload.RootProviderBaseProps>, FileUploadVariantProps>
>(FileUpload.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
  Assign<Assign<HTMLStyledProps<'div'>, FileUpload.RootBaseProps>, FileUploadVariantProps>
>(FileUpload.Root, 'root')
export const Dropzone = withContext<Assign<HTMLStyledProps<'div'>, FileUpload.DropzoneBaseProps>>(
  FileUpload.Dropzone,
  'dropzone',
)
export const ItemDeleteTrigger = withContext<
  Assign<HTMLStyledProps<'button'>, FileUpload.ItemDeleteTriggerBaseProps>
>(FileUpload.ItemDeleteTrigger, 'itemDeleteTrigger')
export const ItemGroup = withContext<Assign<HTMLStyledProps<'ul'>, FileUpload.ItemGroupBaseProps>>(
  FileUpload.ItemGroup,
  'itemGroup',
)
export const ItemName = withContext<Assign<HTMLStyledProps<'div'>, FileUpload.ItemNameBaseProps>>(
  FileUpload.ItemName,
  'itemName',
)
export const ItemPreviewImage = withContext<
  Assign<HTMLStyledProps<'img'>, FileUpload.ItemPreviewImageBaseProps>
>(FileUpload.ItemPreviewImage, 'itemPreviewImage')
export const ItemPreview = withContext<
  Assign<HTMLStyledProps<'div'>, FileUpload.ItemPreviewBaseProps>
>(FileUpload.ItemPreview, 'itemPreview')
export const Item = withContext<Assign<HTMLStyledProps<'li'>, FileUpload.ItemBaseProps>>(
  FileUpload.Item,
  'item',
)
export const ItemSizeText = withContext<
  Assign<HTMLStyledProps<'div'>, FileUpload.ItemSizeTextBaseProps>
>(FileUpload.ItemSizeText, 'itemSizeText')
export const Label = withContext<Assign<HTMLStyledProps<'label'>, FileUpload.LabelBaseProps>>(
  FileUpload.Label,
  'label',
)
export const Trigger = withContext<Assign<HTMLStyledProps<'button'>, FileUpload.TriggerBaseProps>>(
  FileUpload.Trigger,
  'trigger',
)
export {
  FileUploadContext as Context,
  FileUploadHiddenInput as HiddenInput,
} from '@ark-ui/solid'
No snippet found2
Add Re-Export
To improve the developer experience, re-export the styled primitives in~/components/ui/file-upload.tsx.
export * as FileUpload from './styled/file-upload'
export * as FileUpload from './styled/file-upload'
3
Integrate Recipe
If you're not using @park-ui/preset, add the following recipe to yourpanda.config.ts:
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: 'flex-start',
      gridArea: 'delete',
    },
    itemPreview: {
      gridArea: 'preview',
    },
    itemPreviewImage: {
      aspectRatio: '1',
      height: '10',
      objectFit: 'scale-down',
      width: '10',
    },
  },
})