I've read many discussions about image preview in fapi with 'managed_file', but I can't find a conclusive answer on how to do this properly in D8.

An obvious solution would be to reuse Drupal\image\Plugin\Field\FieldWidget\ImageWidget, but I don't know how to accomplish this.

So my question is: how can I have an image upload field with image preview in my form in D8 and what would the associative array in my buildForm function look like?

1 Answer 1


After searching for a while, I have found some kind of workaround solution for this here: https://stackoverflow.com/a/38268567/2267244

I have tested it and it works, here is bit modified version which I have used.

Also I'm not sure if we should mark this question as dpulicate as I found the solution on stackoverflow.com.

Anyway Here is how your form field should looke like:

$form['image_with_preview'] = [
  '#type' => 'managed_file',
  '#title' => t('Image with preview'),
  '#upload_validators' => [
    'file_validate_extensions' => ['gif png jpg jpeg'],
    'file_validate_size' => [25600000],
  '#theme' => 'image_widget',
  '#preview_image_style' => 'medium',
  '#upload_location' => 'public://',
  '#required' => FALSE,

And also you will need to implement hook_preprocess_HOOK in your module like this:

function YOUR_MODULE_preprocess_image_widget(&$variables) {
    $element = $variables['element'];

    $variables['attributes'] = array('class' => array('image-widget', 'js-form-managed-file', 'form-managed-file', 'clearfix'));

    if (!empty($element['fids']['#value'])) {
      $file = reset($element['#files']);
      $element['file_' . $file->id()]['filename']['#suffix'] = ' <span class="file-size">(' . format_size($file->getSize()) . ')</span> ';
      $file_variables = array(
        'style_name' => $element['#preview_image_style'],
        'uri' => $file->getFileUri(),

      // Determine image dimensions.
      if (isset($element['#value']['width']) && isset($element['#value']['height'])) {
        $file_variables['width'] = $element['#value']['width'];
        $file_variables['height'] = $element['#value']['height'];
      } else {
        $image = \Drupal::service('image.factory')->get($file->getFileUri());
        if ($image->isValid()) {
          $file_variables['width'] = $image->getWidth();
          $file_variables['height'] = $image->getHeight();
        else {
          $file_variables['width'] = $file_variables['height'] = NULL;

      $element['preview'] = array(
        '#weight' => -10,
        '#theme' => 'image_style',
        '#width' => $file_variables['width'],
        '#height' => $file_variables['height'],
        '#style_name' => $file_variables['style_name'],
        '#uri' => $file_variables['uri'],

      // Store the dimensions in the form so the file doesn't have to be
      // accessed again. This is important for remote files.
      $element['width'] = array(
        '#type' => 'hidden',
        '#value' => $file_variables['width'],
      $element['height'] = array(
        '#type' => 'hidden',
        '#value' => $file_variables['height'],

    $variables['data'] = array();
    foreach (\Drupal\Core\Render\Element::children($element) as $child) {
      $variables['data'][$child] = $element[$child];

I have tested it on my local Drupal instance and it works fine, image preview was displayed after AJAX upload finished.

  • still works in 2023 in Drupal 10 :) thanks for this! Commented Oct 24, 2023 at 9:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.