Form API image preview 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?
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' =&gt; 'managed_file',
  '#title' =&gt; t('Image with preview'),
  '#upload_validators' =&gt; [
    'file_validate_extensions' =&gt; ['gif png jpg jpeg'],
    'file_validate_size' =&gt; [25600000],
  ],
  '#theme' =&gt; 'image_widget',
  '#preview_image_style' =&gt; 'medium',
  '#upload_location' =&gt; 'public://',
  '#required' =&gt; FALSE,
];


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

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

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

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

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

      $element['preview'] = array(
        '#weight' =&gt; -10,
        '#theme' =&gt; 'image_style',
        '#width' =&gt; $file_variables['width'],
        '#height' =&gt; $file_variables['height'],
        '#style_name' =&gt; $file_variables['style_name'],
        '#uri' =&gt; $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' =&gt; 'hidden',
        '#value' =&gt; $file_variables['width'],
      );
      $element['height'] = array(
        '#type' =&gt; 'hidden',
        '#value' =&gt; $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.