Displaying Exif Data in Craft CMS Templates
When working on a photography website you may need to display the EXIF data with each photo. Exif data is various bits of information saved by the camera whether it's a DLSR, mirrorless, or phone camera. There are a large number of options that you can pull including but not limited to:
- Camera make and model
- Lens make and model
- Focal length
- ISO
- Aperture setting
- Shutter speed
- GPS coordinates
- Copyright info
Preserve Exif Data
The first step is to ensure that Craft CMS is not stripping the exif data from uploaded images which is the default behaviour. To do this you need to add the following line to your config/general.php
.
'preserveExifData' => true,
Unfortunately because the default behavior is to strip exif data from uploaded images, the existing images in the site will not have any exif data to display. On an existing site doing this will mean either replacing existing images with new copies or only displaying EXIF info on new images.
To make this simple I created an exif-data include and pass in the image url like this with a conditional to check on a switch that enables or disables exif data. This is set so that in the articles section of the site exif data can be disabled if it's not important to the image being displayed.
{% if enableExif != false %}
{% include 'photography/_includes/exif-data' with {
imageUrl: image.url
} %}
{% endif %}
Determine what data to display
In order to determine what data to display we need to see what is available for us in the photos we have on the site.
In your template on a page with an image add this and in the dumped code the options will be visible.
{% set getExif = craft.app.images.getExifData(imageUrl ?? image.url) %}
{#
// uncomment to see all EXIF data
#}
{{ dump(getExif) }}
This will return an array of available variables that you can pull from the photo. I found that photos shot with different cameras had different results so you may need to repeate this a few times to get what you are looking for.
Here is an array of exif data from a photo shot on a Fujifilm XT5 and the Fujifilm 23mm F2 lens.
[▼
"uri" => "https://seans-photos.ddev.site/uploads/images/2025-February-057.jpg"
"file.FileDateTime" => 0
"file.FileSize" => 873871
"file.FileType" => 2
"file.MimeType" => "image/jpeg"
"file.SectionsFound" => "ANY_TAG, IFD0, THUMBNAIL, EXIF"
"computed.html" => "width="2000" height="1600""
"computed.Height" => 1600
"computed.Width" => 2000
"computed.IsColor" => 0
"computed.ByteOrderMotorola" => 0
"computed.ApertureFNumber" => "f/4.5"
"computed.Copyright" => "All Rights Reserved 2025"
"computed.Thumbnail.FileType" => 2
"computed.Thumbnail.MimeType" => "image/jpeg"
"computed.Thumbnail.Height" => 150
"computed.Thumbnail.Width" => 150
"ifd0.Exif_IFD_Pointer" => 26
"thumbnail.ImageWidth" => 150
"thumbnail.ImageLength" => 150
"thumbnail.Compression" => 7
"thumbnail.XResolution" => "72/0"
"thumbnail.YResolution" => "72/0"
"thumbnail.ResolutionUnit" => 2
"thumbnail.JPEGInterchangeFormat" => 633
"thumbnail.JPEGInterchangeFormatLength" => 17074
"exif.ImageWidth" => 2000
"exif.ImageLength" => 1600
"exif.BitsPerSample" => 16
"exif.Make" => "FUJIFILM"
"exif.Model" => "X-T5"
"exif.Orientation" => 1
"exif.XResolution" => "96/1"
"exif.YResolution" => "96/1"
"exif.ResolutionUnit" => 2
"exif.DateTime" => "2025:02:28 15:30:14"
"exif.Artist" => null
"exif.Copyright" => "All Rights Reserved 2025"
"exif.ExposureTime" => "1/500"
"exif.FNumber" => "603979776/134217728"
"exif.ExposureProgram" => 3
"exif.ISOSpeedRatings" => 1250
"exif.DateTimeOriginal" => "2025:02:28 15:30:14"
"exif.UndefinedTag:0x9010" => "-5:00"
"exif.UndefinedTag:0x9011" => "-5:00"
"exif.ExposureBiasValue" => "354334816/1073741824"
"exif.MeteringMode" => 2
"exif.FocalLength" => "23/1"
"exif.ColorSpace" => 1
"exif.UndefinedTag:0xA431" => "2DA01540"
"exif.UndefinedTag:0xA433" => "FUJIFILM"
"exif.UndefinedTag:0xA434" => "XF23mmF2 R WR"
]
There's a lot to look through here, but if you know what you want it'll be faster to do a ctrl+f on the page and search for it. What I want are:
- Camera Model
"exif.Model" => "X-T5"
- Lens
"exif.UndefinedTag:0xA434" => "XF23mmF2 R WR"
- ISO
"exif.ISOSpeedRatings" => 1250
- Aperture
"computed.ApertureFNumber" => "f/4.5"
- Shutter Speed
"exif.ExposureTime" => "1/500"
- Focal Length
"exif.FocalLength" => "23/1"
The focal length is often not a usable number like it is here. 23/1
especially if it's a zoom lens. To fix this we need to do a little math like this: it's just dividing the fraction above and then rounding the result with the twig |round filter
.
{# focal length #}
{% if getExif['exif.FNumber'] is defined %}
{% set fnumberParts = getExif['exif.FocalLength']|split('/') %}
{% set fnumber = (fnumberParts[0] / fnumberParts[1]) %}
<span class="flex flex-row gap-2 text-sm">
<span class="sr-only">
Focal Length
</span>
{{ svg('@webroot/assets/images/svg/focal-length.svg')|attr({ class:'h-[1.25rem] w-[1.25rem]'}) }}
{{ fnumber|round }}mm
</span>
{% endif %}

Putting it all together we end up with the screenshot above and the code below.
{# exif data #}
<div class="flex flex-row flex-wrap mb-6 ml-auto gap-x-6 gap-y-2">
{# Display specific EXIF data #}
{# camera #}
{% if getExif['exif.Model'] is defined %}
<span class="flex flex-row gap-2 text-sm">
<span class="sr-only">
Camera
</span><!-- /.sr-only -->
{{ svg('@webroot/assets/images/svg/camera.svg')|attr({ class:'h-4 w-4'}) }} {#{{ getExif['exif.Make'] }}#}{{ getExif['exif.Model'] }}
</span>
{% endif %}
{# lens #}
{% if getExif['exif.UndefinedTag:0xA434'] is defined %}
<span class="relative flex flex-row gap-2 text-xs md:text-sm">
<span class="sr-only">
Lens
</span><!-- /.sr-only -->
{{ svg('@webroot/assets/images/svg/lens2.svg')|attr({ class:'h-6 w-6 relative -top-[3px]'}) }} {#{{ getExif['exif.UndefinedTag:0xA433'] }}#} {{ getExif['exif.UndefinedTag:0xA434']|raw }}
</span>
{% endif %}
{# iso #}
{% if getExif['exif.ISOSpeedRatings'] is defined %}
<span class="relative flex flex-row gap-2 text-sm">
<span class="sr-only">
ISO
</span><!-- /.sr-only -->
{{ svg('@webroot/assets/images/svg/iso.svg')|attr({ class:'h-6 w-6 relative top-[-3px]'}) }} {{ getExif['exif.ISOSpeedRatings'] }}
</span>
{% endif %}
{# aperture #}
{% if getExif['computed.ApertureFNumber'] is defined %}
<span class="flex flex-row gap-2 text-sm">
<span class="sr-only">
Aperture
</span><!-- /.sr-only -->
{{ svg('@webroot/assets/images/svg/aperture.svg')|attr({ class:'h-4 w-4'}) }} {{ getExif['computed.ApertureFNumber'] }}
</span>
{% endif %}
{# focal length #}
{% if getExif['exif.FNumber'] is defined %}
{% set fnumberParts = getExif['exif.FocalLength']|split('/') %}
{% set fnumber = (fnumberParts[0] / fnumberParts[1]) %}
<span class="flex flex-row gap-2 text-sm">
<span class="sr-only">
Focal Length
</span>
{{ svg('@webroot/assets/images/svg/focal-length.svg')|attr({ class:'h-[1.25rem] w-[1.25rem]'}) }}
{{ fnumber|round }}mm
</span>
{% endif %}
{# shutterspeed #}
{% if getExif['exif.ExposureTime'] is defined %}
<span class="flex flex-row gap-2 text-sm">
<span class="sr-only">
Shutter Speed
</span><!-- /.sr-only -->
{{ svg('@webroot/assets/images/svg/shutter-speed.svg')|attr({ class:'h-[1.25rem] w-[1.25rem]'}) }} {{ getExif['exif.ExposureTime'] }}
</span>
{% endif %}
</div>
Note:
Remember that if adding this to an existing site. Images that are already added will not display exif data as it will have been stripped from the file on upload. Once you've done step one of adding this to general.php 'preserveExifData' => true,
all new images will display exif data as expected.