Skip to content

Import

import { Field } from '@dnb/eufemia/extensions/forms'
render(<Field.Upload />)

Description

Field.Upload is a wrapper for the Upload component to make it easier to use inside a form.

There is a corresponding Value.Upload component.

Relevant links

The data and file format

The returned data is an array of objects containing a file object, a unique ID, etc. The file item object contains the file itself and some additional properties like a unique ID.

{
id: '1234',
file: {
name: 'file1.jpg',
size: 1234,
type: 'image/jpeg',
},
// optional properties
exists: true,
isLoading: true,
errorMessage: 'error message',
description: 'description',
removeDeleteButton: true,
}

This data format will be returned by the onChange and the onSubmit event handlers.

Validation

The required property will validate if there are valid files present. If there are files with an error, the validation will fail.

If there are invalid files, the onSubmit event will not be called and a validation error will be shown.

Handling validation errors

Files are automatically validated for file size and file type based on the fileMaxSize and acceptedFileTypes properties. When files fail these validations, they receive an errorMessage property.

You can customize how these invalid files are displayed using the onValidationError callback:

<Field.Upload
fileMaxSize={5}
onValidationError={(invalidFiles) => {
return invalidFiles.map((file) => ({
...file,
removeLink: true, // Remove download link
removeDeleteButton: true, // Remove delete button
description: 'Cannot be uploaded due to validation error',
}))
}}
/>

How it works: The component splits newly uploaded files into two groups based on the presence of an errorMessage property:

  • Files with errorMessage → sent to onValidationError (if defined)
  • Files without errorMessage → sent to fileHandler (if defined)

The errorMessage is typically set by built-in validation (file size or file type checks), but can also be set manually. The two callbacks are mutually exclusive and only process newly added files.

Important: If fileHandler returns a file with an errorMessage, that file is already in the upload list and won't trigger onValidationError again. Handle errors from fileHandler within the fileHandler function itself by returning files with the errorMessage property set.

The onChange event handler will return an array with file item objects containing the file object and some additional properties – regardless of the validity of the file.

For error handling of invalid files, you can refer to the Upload component for more details.

Here is an example of how to use the fileHandler property to validate file sizes.

About the value and path property

The path property represents an array with an object described above:

render(
<Form.Handler defaultData={{ myFiles: files }}>
<Field.Upload path="/myFiles" />
</Form.Handler>
)

The value property represents an array with an object described above:

render(<Field.Upload value={files} />)

About the fileHandler property

The fileHandler is a handler function that supports both asynchronous and synchronous operations. It receives only newly added valid files as a parameter and returns the processed files (or a Promise when asynchronous).

Which files are passed to the handler?

  • Only newly added files: Files that were just uploaded by the user.
  • Only valid files: Files that do not have validation errors such as file size and file type (files with errorMessage are excluded).
  • Not existing files: Files that were previously uploaded and already exist in the list are not included.

What should be returned?

Return an array of file item objects with the same or modified properties:

  • Set a new id: Typically from a server response after uploading (e.g., id: data.server_generated_id).
  • Add errorMessage: To indicate upload failure and display an error message next to the file.
  • Modify other properties: Such as description, removeDeleteButton, removeLink, etc.

Automatic loading state handling

The component automatically handles asynchronous loading states during the upload process, displaying a spinner next to each file while the fileHandler is processing.

async function virusCheck(newFiles) {
const promises = newFiles.map(async (file) => {
const formData = new FormData()
formData.append('file', file.file, file.file.name)
return await fetch('/', { method: 'POST', body: formData })
.then((response) => {
if (response.ok) return response.json()
throw new Error('Unable to upload this file')
})
.then((data) => {
return {
...file,
id: data.server_generated_id,
}
})
.catch((error) => {
return {
...file,
errorMessage: error.message,
}
})
})
return await Promise.all(promises)
}

TransformIn and TransformOut

You can use the transformIn and transformOut properties to transform the data from the internal format to the external format and vice versa.

import { Form, Field, Tools } from '@dnb/eufemia/extensions/forms'
import type {
UploadValue,
UploadFileNative,
} from '@dnb/eufemia/extensions/forms/Field/Upload'
// Our external format
type DocumentMetadata = {
id: string
fileName: string
}
const defaultValue = [
{
id: '1234',
fileName: 'myFile.pdf',
},
] satisfies DocumentMetadata[] as unknown as UploadValue
const filesCache = new Map<string, File>()
// To the Field (from e.g. defaultValue)
const transformIn = (external?: DocumentMetadata[]) => {
return (
external?.map(({ id, fileName }) => {
const file: File =
filesCache.get(id) ||
new File([], fileName, { type: 'images/png' })
return { id, file }
}) || []
)
}
// From the Field (internal value) to the data context or event parameter
const transformOut = (internal?: UploadValue) => {
return (
internal?.map(({ id, file }) => {
if (!filesCache.has(id)) {
filesCache.set(id, file)
}
return { id, fileName: file.name }
}) || []
)
}
function MyForm() {
return (
<Form.Handler>
<Field.Upload
path="/documents"
transformIn={transformIn}
transformOut={transformOut}
defaultValue={defaultValue}
/>
<Tools.Log />
</Form.Handler>
)
}

TransformIn considerations

The properties of the file item object is used internally to visually customize the file when displayed. For instance when displaying a spinner when isLoading: true. It does also exist some internal logic based on these values, so be careful when changing these through transformers, like transformIn, as changing or overriding these properties could have unexpected results. If doing so, it's recommended to pass along the rest of the file item object using the spread operator (...fileItemObj) or so, as it can contain properties needed internally that one is not aware of, or updated values since last file was uploaded, or even future new internal properties that does not exist yet.

<Form.Handler>
<Field.Upload
path="/documents"
transformIn={(value) => {
return (value || []).map((fileItemObj) => ({
...fileItemObj,
errorMessage: 'error message',
}))
}}
/>
</Form.Handler>

Persist files in session storage

The sessionStorageId property can be used to store the files in the session storage so they persist between page reloads.

But the persisted files only render the file name, and not the file itself. The file blob will be lost during the serialization process.

Demos

Consider taking a look at the demos for the Upload component as well.

Basic usage

My custom label

My description

Tillatte filformater:
JPEG, JPG, PDF, PNG
Maks filstørrelse:
5 MB
Code Editor
<Form.Handler>
  <Field.Upload
    label="My custom label"
    labelDescription="My description"
    onChange={(files) => console.log('onChange', files)}
  />
</Form.Handler>

Variant compact

Code Editor
<Form.Handler>
  <Field.Upload
    variant="compact"
    label="My custom label"
    labelDescription="My description"
    onChange={(files) => console.log('onChange', files)}
  />
</Form.Handler>

Required

Last opp dokumenter

Dra og slipp eller velg hvilke filer du vil laste opp.

Tillatte filformater:
JPEG, JPG, PDF, PNG
Maks filstørrelse:
5 MB
Code Editor
<Form.Handler onSubmit={(data) => console.log('onSubmit', data)}>
  <Flex.Stack>
    <Field.Upload path="/myFiles" required />
    <Form.SubmitButton />
  </Flex.Stack>
</Form.Handler>

Path usage

Last opp dokumenter

Dra og slipp eller velg hvilke filer du vil laste opp.

Tillatte filformater:
JPEG, JPG, PDF, PNG
Maks filstørrelse:
5 MB
Code Editor
<Form.Handler
  onChange={(data) => console.log('onChange', data)}
  data={{
    myFiles: [
      {
        file: createMockFile('fileName-1.png', 100, 'image/png'),
      },
    ],
  }}
>
  <Field.Upload path="/myFiles" />
</Form.Handler>

With help

Last opp dokumenter

Dra og slipp eller velg hvilke filer du vil laste opp.

Help title

Help content

Tillatte filformater:
JPEG, JPG, PDF, PNG
Maks filstørrelse:
5 MB
Code Editor
<Field.Upload
  help={{
    open: true,
    title: 'Help title',
    content: 'Help content',
  }}
/>

Customized

Warning message

My custom title

My text with a help button

Help title

Help content

Tillatte filformater:
PDF
Maks filstørrelse:
1 MB
Maks antall filer:
1
Code Editor
<Field.Upload
  title="My custom title"
  text="My text with a help button"
  width="large"
  help={{
    title: 'Help title',
    content: 'Help content',
    open: true,
  }}
  warning="Warning message"
  acceptedFileTypes={['pdf']}
  filesAmountLimit={1}
  fileMaxSize={1}
/>

Session storage support

The sessionStorageId property can be used to store the files in the session storage so they persist between page reloads.

Last opp dokumenter

Dra og slipp eller velg hvilke filer du vil laste opp.

Tillatte filformater:
JPEG, JPG, PDF, PNG
Maks filstørrelse:
5 MB
Uploaded filesNo files uploaded.
"undefined" 
Code Editor
<Form.Handler sessionStorageId="documents">
  <Flex.Stack>
    <Form.Card>
      <Field.Upload path="/documents" />
      <Value.Upload
        path="/documents"
        label="Uploaded files"
        placeholder="No files uploaded."
        variant="ol"
        showEmpty
      />
    </Form.Card>

    <Form.SubmitButton />
    <Tools.Log />
  </Flex.Stack>
</Form.Handler>

With asynchronous file handler

The fileHandler property supports an asynchronous function, and can be used for handling/validating files asynchronously, like to upload files to a virus checker and display errors based on the outcome:

Last opp dokumenter

Upload multiple files at once to see the upload error message. This demo has been set up so that every other file in a batch will fail.

Tillatte filformater:
JPEG, JPG, PDF, PNG
Maks filstørrelse:
5 MB
"undefined" 
Code Editor
const MyForm = () => {
  return (
    <Form.Handler onSubmit={async (form) => console.log(form)}>
      <Flex.Stack>
        <Field.Upload
          path="/attachments"
          labelDescription="Upload multiple files at once to see the upload error message. This demo has been set up so that every other file in a batch will fail."
          fileHandler={mockAsyncFileUpload}
          required
        />
        <Form.SubmitButton />
        <Tools.Log />
      </Flex.Stack>
    </Form.Handler>
  )
}
async function mockAsyncFileUpload(
  newFiles: UploadValue
): Promise<UploadValue> {
  const updatedFiles: UploadValue = []
  for (const [index, file] of Object.entries(newFiles)) {
    const formData = new FormData()
    formData.append('file', file.file, file.file.name)
    const request = createRequest()
    await request(Math.floor(Math.random() * 2000) + 1000) // Simulate a request

    try {
      const mockResponse = {
        ok: (parseFloat(index) + 2) % 2 === 0,
        // Every other request will fail
        json: async () => ({
          server_generated_id: file.file.name + '_' + crypto.randomUUID(),
        }),
      }
      if (!mockResponse.ok) {
        throw new Error('Unable to upload this file')
      }
      const data = await mockResponse.json()
      updatedFiles.push({
        ...file,
        id: data.server_generated_id,
      })
    } catch (error) {
      updatedFiles.push({
        ...file,
        errorMessage: error.message,
        removeLink: true,
      })
    }
  }
  return updatedFiles
}
render(<MyForm />)

With synchronous file handler

The fileHandler property supports a synchronous function, and can be used for handling/validating files synchronously, like to check for file names that's too long:

Last opp dokumenter

Dra og slipp eller velg hvilke filer du vil laste opp.

Tillatte filformater:
JPEG, JPG, PDF, PNG
Maks filstørrelse:
5 MB
"undefined" 
Code Editor
const MyForm = () => {
  return (
    <Form.Handler onSubmit={async (form) => console.log(form)}>
      <Flex.Stack>
        <Field.Upload
          path="/myattachments"
          fileHandler={mockSyncFileUpload}
          required
        />
        <Form.SubmitButton />
        <Tools.Log />
      </Flex.Stack>
    </Form.Handler>
  )
}
function mockSyncFileUpload(newFiles: UploadValue) {
  return newFiles.map((file) => {
    if (file.file.name.length > 5) {
      file.errorMessage = 'File name is too long'
    }
    return file
  })
}
render(<MyForm />)

With asynchronous onFileDelete

Last opp dokumenter

Dra og slipp eller velg hvilke filer du vil laste opp.

Tillatte filformater:
JPG, PNG
Maks filstørrelse:
5 MB
Code Editor
async function mockAsyncFileRemoval({ fileItem }) {
  const request = createRequest()
  console.log(`making API request to remove: ${fileItem.file.name}`)
  await request(3000) // Simulate a request
  const mockResponse = {
    successful_removal: Math.random() < 0.5, // Randomly fails to remove the file
  }

  if (!mockResponse.successful_removal) {
    throw new Error('Unable to remove this file')
  }
}
render(
  <Field.Upload
    onFileDelete={mockAsyncFileRemoval}
    acceptedFileTypes={['jpg', 'png']}
  />
)

With asynchronous onFileClick

Last opp dokumenter

Dra og slipp eller velg hvilke filer du vil laste opp.

Tillatte filformater:
JPEG, JPG, PDF, PNG
Maks filstørrelse:
5 MB
Code Editor
async function mockAsyncFileClick({ fileItem }) {
  const request = createRequest()
  console.log(
    `making API request to fetch the url of the file: ${fileItem.file.name}`
  )
  await request(2000) // Simulate a request
  window.open(
    `https://eufemia.dnb.no/images/avatars/${fileItem.file.name}`,
    '_blank'
  )
}
render(
  <Form.Handler
    data={{
      myFiles: [
        {
          file: createMockFile('1501870.jpg', 100, 'image/png'),
          id: '1',
        },
      ],
    }}
  >
    <Field.Upload path="/myFiles" onFileClick={mockAsyncFileClick} />
  </Form.Handler>
)

With FileItem options

Last opp dokumenter

Dra og slipp eller velg hvilke filer du vil laste opp.

Tillatte filformater:
JPEG, JPG, PDF, PNG
Maks filstørrelse:
5 MB
Code Editor
const MyForm = () => {
  return (
    <Form.Handler
      data={{
        myFiles: [
          {
            file: createMockFile('fileName-1.png', 100, 'image/png'),
            id: '1',
            description: 'My description',
            errorMessage: 'My error message',
            removeDeleteButton: true,
            removeLink: true,
          },
        ],
      }}
    >
      <Field.Upload path="/myFiles" fileHandler={fileHandler} required />
    </Form.Handler>
  )
}
function fileHandler(newFiles: UploadValue) {
  return newFiles.map((file) => {
    file.errorMessage = 'File has a problem'
    file.description = 'File description'
    file.removeDeleteButton = true
    return file
  })
}
render(<MyForm />)

With file size validation

Label

This is a Field

Tillatte filformater:
PNG
Code Editor
const MAX_SIZE = 500 * 1024 // 500 KB
const MIN_SIZE = 50 * 1024 // 50 KB

const myTranslation = {
  'nb-NO': {
    errorFileTooSmall: 'Filen er for liten.',
    errorFileTooLarge: 'Filen er for stor.',
  },
  'en-GB': {
    errorFileTooSmall: 'File is too small.',
    errorFileTooLarge: 'File is too large.',
  },
}
function MyField() {
  const tr = Form.useTranslation()
  const fileHandler = (newFiles: UploadValue) => {
    return newFiles.map((item) => {
      console.log('item:', item)
      if (item.file.size < MIN_SIZE) {
        item.errorMessage = tr['errorFileTooSmall']
      }
      if (item.file.size > MAX_SIZE) {
        item.errorMessage = tr['errorFileTooLarge']
      }
      return item
    })
  }
  return (
    <Field.Upload
      label="Label"
      labelDescription="This is a Field"
      path="/myField"
      acceptedFileTypes={['PNG']}
      fileMaxSize={false}
      fileHandler={fileHandler}
    />
  )
}
render(
  <Form.Handler
    translations={myTranslation}
    onSubmit={(data) => console.log('onSubmit', data)}
  >
    <Form.Card>
      <MyField />
    </Form.Card>

    <Form.SubmitButton />
  </Form.Handler>
)

With validation error handler

The onValidationError property can be used to handle files that fail built-in validation (file size or file type). This allows you to customize the appearance and behavior of invalid files, such as removing the download link or adding custom descriptions:

Upload documents

Try uploading files larger than 1 MB or unsupported file types (e.g., .docx) to see validation error handling.

Tillatte filformater:
JPG, PDF, PNG
Maks filstørrelse:
1 MB
"undefined" 
Code Editor
function validationErrorHandler(invalidFiles: UploadValue): UploadValue {
  return invalidFiles.map((file) => ({
    ...file,
    removeLink: true,
    description: 'This file cannot be uploaded due to validation failure',
  }))
}
async function fileHandler(validFiles: UploadValue): Promise<UploadValue> {
  const updatedFiles: UploadValue = []
  for (const file of validFiles) {
    const request = createRequest()
    await request(2000) // Simulate upload

    updatedFiles.push({
      ...file,
      id: `server_${crypto.randomUUID()}`,
    })
  }
  return updatedFiles
}
async function onFileDelete({ fileItem }) {
  const request = createRequest()
  console.log('Deleting file:', fileItem.file.name)
  await request(1000) // Simulate delete
}
render(
  <Form.Handler onSubmit={(data) => console.log('onSubmit', data)}>
    <Flex.Stack>
      <Field.Upload
        path="/myFiles"
        fileMaxSize={1}
        acceptedFileTypes={['jpg', 'pdf', 'png']}
        label="Upload documents"
        labelDescription="Try uploading files larger than 1 MB or unsupported file types (e.g., .docx) to see validation error handling."
        onValidationError={validationErrorHandler}
        fileHandler={fileHandler}
        onFileDelete={onFileDelete}
      />
      <Form.SubmitButton />
      <Tools.Log />
    </Flex.Stack>
  </Form.Handler>
)

With Iterate.Array

Required field with async fileHandler

Dra og slipp eller velg hvilke filer du vil laste opp.

Tillatte filformater:
JPEG, JPG, PDF, PNG
Maks filstørrelse:
5 MB

Required field with async fileHandler

Dra og slipp eller velg hvilke filer du vil laste opp.

Tillatte filformater:
JPEG, JPG, PDF, PNG
Maks filstørrelse:
5 MB
{
  "listOfFiles": [
    {
      "files": "undefined"
    },
    {
      "files": "undefined"
    }
  ]
} 
Code Editor
async function mockAsyncFileUpload(
  newFiles: UploadFile[]
): Promise<UploadFile[]> {
  const updatedFiles: UploadFile[] = []
  for (const [, file] of Object.entries(newFiles)) {
    const formData = new FormData()
    formData.append('file', file.file, file.file.name)
    const request = createRequest()
    await request(8000) // Simulate a request

    try {
      const mockResponse = {
        ok: true,
        json: async () => ({
          server_generated_id: file.file.name + '_' + crypto.randomUUID(),
        }),
      }
      const data = await mockResponse.json()
      updatedFiles.push({
        ...file,
        id: data.server_generated_id,
      })
    } catch (error) {
      updatedFiles.push({
        ...file,
        errorMessage: error.message,
      })
    }
  }
  return updatedFiles
}
async function mockAsyncOnFileClick({ fileItem }) {
  const request = createRequest()
  console.log(
    'making API request to fetch the url of the file: ' +
      fileItem.file.name
  )
  await request(3000) // Simulate a request
  window.open(
    'https://eufemia.dnb.no/images/avatars/1501870.jpg',
    '_blank'
  )
}
async function mockAsyncFileRemoval({ fileItem }) {
  const request = createRequest()
  console.log('Making API request to remove: ' + fileItem.file.name)
  await request(3000) // Simulate a request
}
render(
  <Form.Handler
    onSubmit={(data) => {
      console.log('submitted data:', data)
    }}
    defaultData={{
      listOfFiles: [
        {
          files: undefined,
        },
        {
          files: undefined,
        },
      ],
    }}
  >
    <Iterate.Array path="/listOfFiles">
      <Field.Upload
        itemPath="/files"
        label="Required field with async fileHandler"
        onFileDelete={mockAsyncFileRemoval}
        onFileClick={mockAsyncOnFileClick}
        fileHandler={mockAsyncFileUpload}
        required
        onChange={(e) => {
          console.log('onChange', e)
        }}
      />
    </Iterate.Array>
    <Form.SubmitButton />
    <Tools.Log />
  </Form.Handler>
)