> Markdown version of [Image Fields in Forms](https://vaadin.com/docs/next/hilla/guides/forms/form-images). Section index: [llms.txt](https://vaadin.com/docs/next/hilla/llms.txt)

# Submitting Images in Forms

A common use case is a form field that allows an image to be uploaded. For instance, an application may let users customize their avatars. This page shows how this can be implemented for forms, using the `vaadin-upload` component for local file selection (by file browser or dragging), but postponing the actual server upload until the form is submitted.

The example application allows a contact card to be edited. It assumes a server-side bean where the image is stored as a Base64-encoded string in the field `avatarBase64`:

`Contact.java`

```java
package com.vaadin.demo.fusion.forms;

/**
 * Contact card with base64-encoded image
 */
public class Contact {
    // other contact fields: name, address, phone, ...

    private String avatarBase64;

    public String getAvatarBase64() {
        return avatarBase64;
    }

    public void setAvatarBase64(String avatarBase64) {
        this.avatarBase64 = avatarBase64;
    }
}
```

It also assumes that the server exposes a browser-callable service for saving updated `Contact` instances:

`ContactService.java`

```java
package com.vaadin.demo.fusion.forms;

import com.vaadin.hilla.BrowserCallable;

@BrowserCallable
public class ContactService {
    // other service methods: read, delete, ...

    public void saveContact(Contact contact) {
        // persistently store the contact
    }
}
```

The following form binds the `avatarBase64` field of the instance to a `vaadin-upload` component:

```tsx
import ContactModel from 'Frontend/generated/com/vaadin/demo/fusion/forms/ContactModel';
import { ContactService } from 'Frontend/generated/endpoints';
import { useForm } from '@vaadin/hilla-react-form';
import { useEffect } from 'react';
import { Button } from '@vaadin/react-components/Button.js';
import { Upload, type UploadBeforeEvent } from '@vaadin/react-components/Upload.js';
import { readAsDataURL } from 'promise-file-reader';


export default function ContactForm() {

  const form = useForm(ContactModel, {
    onSubmit: async (contact) => {
      await ContactService.saveContact(contact);
    },
  });

  useEffect(() => {
    ContactService.loadContact().then(form.read);
  }, []);

  return (
    <div>
      <img src={form.value?.avatarBase64} alt="contact's avatar" />
      <Upload
        capture="camera"
        accept="image/*"
        max-files="1"
        onUploadBefore={async (e: UploadBeforeEvent) => {
          const file = e.detail.file;
          e.preventDefault();
          if (form.value) {
            form.value.avatarBase64 = await readAsDataURL(file);
          }
        }}
      />
      <Button onClick={form.submit}>Save</Button>
    </div>
  );
}
```

In the above code, the custom `onUploadBefore` listener prevents `Upload` from uploading the received file to the server. Instead, it reads the file into a Base64-encoded string and updates the form field `avatarBase64` via the `Binder`. The small [`promise-file-reader`](https://www.npmjs.com/package/promise-file-reader) library wrapping `FileReader` inside a promise is used here to handle the result synchronously.

The `Contact` instance is first submitted to the `saveContact()` service method. This happens through the `binder.submit` that is called upon clicking the Save button which executes the provided logic for `onSubmit` while obtaining the binder instance.

Only then does the server-side service implementation receive the image string. The server can then choose to recode the image for more efficient storage, if necessary.

The advantage of using the string type is simplicity; you can use the built-in serialization mechanism of Hilla’s form binder and services. This approach isn’t suitable for larger files. A streamed upload may be more appropriate.
