Paste an image into an HTML form as a reviewable attachment

To accept a screenshot pasted into a form, read the paste event's clipboardData.items, keep items whose kind is file, and call getAsFile(). A copied image may arrive as an image file; a copied image URL may arrive only as text. Keep a file picker available for that second case and for browsers that do not expose the clipboard image.

A fictional maintenance report needs one photo beside a written fault description. Pasting a screenshot should replace the report's attachment preview without inserting an image into the description. The attachment remains local until the application's existing upload process sends it.

Give the paste action a clear destination

Use a labeled field rather than intercepting every paste on the document. Staff can then paste an order number into another field without triggering an attachment operation.

<label for="fault">Fault description; paste a screenshot here to attach it</label>
<textarea id="fault" name="fault" rows="4"></textarea>
<label for="photo">Or choose a screenshot</label>
<input id="photo" type="file" accept="image/png,image/jpeg,image/webp">
<p id="attachment-status" role="status">No attachment selected.</p>
<img id="attachment-preview" alt="Selected report attachment" hidden>
<button id="remove-photo" type="button">Remove attachment</button>

This is one attachment slot, shared by paste and file selection. accept helps the picker show suitable files; the script still checks the selected file. The server must validate an eventual upload independently. The HTML input types guide explains the picker and its file collection.

Keep the File, and use a temporary URL only for its preview

The following script belongs after the markup. Its deliberately narrow policy accepts PNG, JPEG, or WebP up to 5 MiB. Adjust that policy to match the upload endpoint before integrating it.

const fault = document.querySelector('#fault');
const picker = document.querySelector('#photo');
const preview = document.querySelector('#attachment-preview');
const status = document.querySelector('#attachment-status');
const remove = document.querySelector('#remove-photo');
const allowed = new Set(['image/png', 'image/jpeg', 'image/webp']);
let attachment = null;
let previewURL = null;

function releasePreview() {
  if (previewURL) URL.revokeObjectURL(previewURL);
  previewURL = null;
  preview.removeAttribute('src');
  preview.hidden = true;
}

function choose(file) {
  if (!allowed.has(file.type) || file.size > 5 * 1024 * 1024) {
    status.textContent = 'Choose PNG, JPEG, or WebP up to 5 MiB. Previous attachment kept.';
    return false;
  }
  releasePreview();
  attachment = file;
  previewURL = URL.createObjectURL(file);
  preview.src = previewURL;
  preview.hidden = false;
  status.textContent = `Attachment selected: ${file.name || 'pasted image'}`;
  return true;
}

fault.addEventListener('paste', event => {
  const files = Array.from(event.clipboardData?.items || [])
    .filter(item => item.kind === 'file')
    .map(item => item.getAsFile())
    .filter(Boolean);
  if (!files.length) return; // ordinary text paste stays native
  const image = files.find(file => allowed.has(file.type));
  if (!image) {
    status.textContent = 'No supported image file found. Use the file picker.';
    return;
  }
  event.preventDefault(); // handle this image-bearing paste as an attachment
  if (choose(image)) picker.value = '';
});

picker.addEventListener('change', () => {
  const file = picker.files[0];
  if (file) choose(file);
  picker.value = ''; // permit choosing the same file again
});

remove.addEventListener('click', () => {
  releasePreview();
  attachment = null;
  picker.value = '';
  status.textContent = 'No attachment selected.';
});

The handler leaves a paste with no file items alone. When a paste includes a supported image, it consumes that operation and selects the first supported image. Any accompanying clipboard text is not inserted by that same operation. Tell users about that choice if their source commonly copies text and an image together.

URL.createObjectURL() creates a browser-local reference to the file. It does not upload anything or create a link a colleague can open. Replacing or removing the attachment revokes the old URL. For an application that removes this form without unloading the page, also call releasePreview() during component cleanup. Object URL documentation

Pass the file into the existing upload route

Do not send the blob: preview URL as the attachment. Send the retained File through the application's normal request and inspect the server response before marking the report saved.

For example, inside an existing submission handler:

const body = new FormData();
body.append('fault', fault.value);
if (attachment) {
  body.append('photo', attachment, attachment.name || 'pasted-image.png');
}
// Send body through the application's authenticated upload route.

The selected file is kept in a JavaScript variable, so a plain native form submission will not automatically include it. The snippet above shows the request body boundary, not a working storage service. Keep the endpoint's file checks, authorization, and saved-record behavior intact.

Distinguish an image file from an image reference

Copying pixels from a screenshot tool can expose a file item. Copying an image from a web page can expose a URL, HTML, a file, or a combination, depending on the source and browser. getAsFile() returns a File only for a file item. It does not download a URL found in clipboard text. Clipboard data, file extraction

Keep ordinary text unchanged rather than trying to fetch anything that resembles an image URL. If no preview appears, choose the saved screenshot with the picker. A rich-text document that should embed images in its body needs a different content and upload workflow; the TinyMCE paste policy guide covers that editor's paste decisions.

Before sending the maintenance report, select a screenshot, replace it with another, remove it, and choose the original again. Confirm that the description survives each change and that the saved report contains the final chosen file. A visible local preview is evidence of a selection, not evidence of a completed upload.

Keep reading