Using the Managed File Element for Drupal-Tracked Uploadsfor Drupal 11 , 10 , 9 , and 8

Last updated :  

The previous lesson's plain file element hands you a raw, temporary upload and leaves the saving to you. managed_file is the version most real Drupal modules actually use — it wraps that same mechanism with automatic AJAX saving, a live preview, and a remove button, turning file uploads from a manual chore into a couple of array keys.

What you'll learn in this lesson

  • The managed_file definition and where it stores files by default
  • How the submitted value differs from a plain file element — you get a file ID, not raw upload data
  • Marking an uploaded file permanent so it survives Drupal's automatic cleanup

A basic managed_file element

$form['photo'] = [
  '#type' => 'managed_file',
  '#title' => $this->t('Profile photo'),
  '#upload_location' => 'public://profile-photos/',
  '#upload_validators' => [
    'file_validate_extensions' => ['png jpg jpeg'],
    'file_validate_size' => [2 * 1024 * 1024],
  ],
];

#upload_location tells Drupal exactly where to save the file — public:// resolves to your site's public files directory, and the AJAX-driven save happens automatically the moment the user selects a file, well before the surrounding form is even submitted.

The submitted value: a file ID, not raw data

$form_state->getValue('photo') returns an array of file entity IDs (fids) — typically one, unless you've set #multiple. Load the actual managed File entity from that ID whenever you need its real properties:

$fids = $form_state->getValue('photo');
if (!empty($fids)) {
  $file = \Drupal\file\Entity\File::load(reset($fids));
}

Making the file permanent

Here's the detail that trips people up the first time: a freshly uploaded managed file starts life as temporary, and Drupal's own cron job deletes unreferenced temporary files after a set period (24 hours by default). If your submit handler doesn't explicitly attach the file somewhere permanent, the upload can vanish before anyone notices.

$file->setPermanent();
$file->save();

Saving the file's ID onto a content entity's file/image field (rather than a plain config value) usually marks it permanent for you automatically, via Drupal's file usage tracking — but if you're storing the fid somewhere else entirely (like a config object), call setPermanent() yourself or the file will quietly disappear.

This is exactly the mechanism our own image-upload course used all session — every screenshot embedded in this course's own lessons went through this same managed_file/permanent-marking pattern under the hood, via the platform's content-import API.

Quick check: a user uploads a file via managed_file, but your submit handler saves the fid into a plain config value rather than a content entity's file field, and never calls setPermanent(). What happens to the file within a day? (It gets deleted by Drupal's own cron cleanup — an uploaded managed file starts temporary, and nothing marked it permanent, so the automatic garbage collection removes it.)

Key takeaways

  • managed_file saves the upload automatically via AJAX, well before the form is even submitted — you get back a file ID, not raw upload data.
  • A newly uploaded file starts temporary and is deleted by cron within a set window unless something marks it permanent.
  • Attaching a file's ID to a content entity's file field typically marks it permanent automatically via file usage tracking; anywhere else, call setPermanent() yourself.

Coming up next

You've now covered file uploads end to end. Next, we look at referencing other content entirely — the entity autocomplete/reference field.