Digging in Twig certainly works, though it does have limitations (such as translation support).
If you need custom output, you could try:
function getMediaFieldImage($mediaField = NULL, $imageStyle = NULL) {
if ($mediaField && $mediaField->entity) {
$langcode = \Drupal::languageManager()->getCurrentLanguage()->getId();
// Get image.
$mediaImage = $mediaField->entity
->get('field_media_image');
// Check for translation.
if ($mediaField->entity->hasTranslation($langcode)) {
// Get translated image field.
$mediaImage = $mediaField->entity->getTranslation($langcode)->get('field_media_image');
}
// Get image attributes.
$alt = $mediaImage->alt;
$width = $mediaImage->width;
$height = $mediaImage->height;
$title = $mediaImage->title;
// Get image entity.
$mediaImageEntity = $mediaImage->entity;
// Get image mime type.
$mediaImageMimeType = $mediaImageEntity->getMimeType();
// Get image file URI.
$mediaImageEntityUri = $mediaImageEntity->getFileUri();
// Set image URL.
$url = \Drupal::service('file_url_generator')->generateString($mediaImageEntityUri);
// Set image style URL if not .svg.
if ($imageStyle && $mediaImageMimeType !== "image/svg+xml") {
$url = \Drupal::service('entity_type.manager')
->getStorage('image_style')
->load($imageStyle)
->buildUrl($mediaImageEntityUri);
}
return [
'url' => $url,
'alt' => $alt,
'width' => $width,
'height' => $height,
'mimeType' => $mediaImageMimeType,
'title' => $title,
];
}
return NULL;
}
Set it like:
function MYTHEME_preprocess_paragraph(&$variables) {
$variables['mediaImage'] = getMediaFieldImage($paragraph->get('field_name'), 'medium');
}
And in Twig:
<img src="{{ mediaImage.url }}" width="{{ mediaImage.width }}" height="{{ mediaImage.height }}" alt="{{ mediaImage.alt }}" />