diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
index 2219a43074362834..23fdafb690117d91 100644
--- a/src/libcamera/camera_sensor.cpp
+++ b/src/libcamera/camera_sensor.cpp
@@ -131,7 +131,7 @@ int CameraSensor::init()
 	properties_.set(properties::Rotation, propertyValue);
 
 	/* Enumerate and cache media bus codes and sizes. */
-	const ImageFormats formats = subdev_->formats(0);
+	const ImageFormats<unsigned int> formats = subdev_->formats(0);
 	if (formats.isEmpty()) {
 		LOG(CameraSensor, Error) << "No image format found";
 		return -EINVAL;
diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp
index 5f6552a4e06c5231..98817deee2b54c84 100644
--- a/src/libcamera/formats.cpp
+++ b/src/libcamera/formats.cpp
@@ -39,7 +39,8 @@ namespace libcamera {
  * \return 0 on success or a negative error code otherwise
  * \retval -EEXIST The format is already described
  */
-int ImageFormats::addFormat(unsigned int format, const std::vector<SizeRange> &sizes)
+template<typename T>
+int ImageFormats<T>::addFormat(T format, const std::vector<SizeRange> &sizes)
 {
 	if (data_.find(format) != data_.end())
 		return -EEXIST;
@@ -53,7 +54,8 @@ int ImageFormats::addFormat(unsigned int format, const std::vector<SizeRange> &s
  * \brief Check if the list of devices supported formats is empty
  * \return True if the list of supported formats is empty
  */
-bool ImageFormats::isEmpty() const
+template<typename T>
+bool ImageFormats<T>::isEmpty() const
 {
 	return data_.empty();
 }
@@ -62,9 +64,10 @@ bool ImageFormats::isEmpty() const
  * \brief Retrieve a list of all supported image formats
  * \return List of pixel formats or media bus codes
  */
-std::vector<unsigned int> ImageFormats::formats() const
+template<typename T>
+std::vector<T> ImageFormats<T>::formats() const
 {
-	std::vector<unsigned int> formats;
+	std::vector<T> formats;
 	formats.reserve(data_.size());
 
 	/* \todo: Should this be cached instead of computed each time? */
@@ -84,7 +87,8 @@ std::vector<unsigned int> ImageFormats::formats() const
  * \return The list of image sizes supported for \a format, or an empty list if
  * the format is not supported
  */
-const std::vector<SizeRange> &ImageFormats::sizes(unsigned int format) const
+template<typename T>
+const std::vector<SizeRange> &ImageFormats<T>::sizes(T format) const
 {
 	static const std::vector<SizeRange> empty;
 
@@ -99,9 +103,12 @@ const std::vector<SizeRange> &ImageFormats::sizes(unsigned int format) const
  * \brief Retrieve the map that associates formats to image sizes
  * \return The map that associates formats to image sizes
  */
-const std::map<unsigned int, std::vector<SizeRange>> &ImageFormats::data() const
+template<typename T>
+const std::map<T, std::vector<SizeRange>> &ImageFormats<T>::data() const
 {
 	return data_;
 }
 
+template class ImageFormats<unsigned int>;
+
 } /* namespace libcamera */
diff --git a/src/libcamera/include/formats.h b/src/libcamera/include/formats.h
index f43bc8c004f690b4..566be9c09550a830 100644
--- a/src/libcamera/include/formats.h
+++ b/src/libcamera/include/formats.h
@@ -15,18 +15,19 @@
 
 namespace libcamera {
 
+template<typename T>
 class ImageFormats
 {
 public:
-	int addFormat(unsigned int format, const std::vector<SizeRange> &sizes);
+	int addFormat(T format, const std::vector<SizeRange> &sizes);
 
 	bool isEmpty() const;
-	std::vector<unsigned int> formats() const;
-	const std::vector<SizeRange> &sizes(unsigned int format) const;
-	const std::map<unsigned int, std::vector<SizeRange>> &data() const;
+	std::vector<T> formats() const;
+	const std::vector<SizeRange> &sizes(T format) const;
+	const std::map<T, std::vector<SizeRange>> &data() const;
 
 private:
-	std::map<unsigned int, std::vector<SizeRange>> data_;
+	std::map<T, std::vector<SizeRange>> data_;
 };
 
 } /* namespace libcamera */
diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h
index 9c077674f997dae6..53c73617efe4b860 100644
--- a/src/libcamera/include/v4l2_subdevice.h
+++ b/src/libcamera/include/v4l2_subdevice.h
@@ -44,7 +44,7 @@ public:
 	int setCrop(unsigned int pad, Rectangle *rect);
 	int setCompose(unsigned int pad, Rectangle *rect);
 
-	ImageFormats formats(unsigned int pad);
+	ImageFormats<unsigned int> formats(unsigned int pad);
 
 	int getFormat(unsigned int pad, V4L2SubdeviceFormat *format);
 	int setFormat(unsigned int pad, V4L2SubdeviceFormat *format);
diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h
index fcf072641420dacf..982df9d2f918e49c 100644
--- a/src/libcamera/include/v4l2_videodevice.h
+++ b/src/libcamera/include/v4l2_videodevice.h
@@ -180,7 +180,7 @@ public:
 
 	int getFormat(V4L2DeviceFormat *format);
 	int setFormat(V4L2DeviceFormat *format);
-	ImageFormats formats();
+	ImageFormats<PixelFormat> formats();
 
 	int setCrop(Rectangle *rect);
 	int setCompose(Rectangle *rect);
diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp
index cf419e8b25a87389..8efd188e75a3d135 100644
--- a/src/libcamera/pipeline/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo.cpp
@@ -155,7 +155,7 @@ CameraConfiguration *PipelineHandlerUVC::generateConfiguration(Camera *camera,
 	if (roles.empty())
 		return config;
 
-	ImageFormats v4l2formats = data->video_->formats();
+	ImageFormats<PixelFormat> v4l2formats = data->video_->formats();
 	StreamFormats formats(v4l2formats.data());
 	StreamConfiguration cfg(formats);
 
diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp
index 93d89729e1faa29f..9fbe33c626e327d4 100644
--- a/src/libcamera/pipeline/vimc.cpp
+++ b/src/libcamera/pipeline/vimc.cpp
@@ -175,7 +175,7 @@ CameraConfiguration *PipelineHandlerVimc::generateConfiguration(Camera *camera,
 	if (roles.empty())
 		return config;
 
-	ImageFormats formats;
+	ImageFormats<PixelFormat> formats;
 
 	for (PixelFormat pixelformat : pixelformats) {
 		/* The scaler hardcodes a x3 scale-up ratio. */
diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp
index f2bcd7f73c5c75a1..bec14f80c960d854 100644
--- a/src/libcamera/v4l2_subdevice.cpp
+++ b/src/libcamera/v4l2_subdevice.cpp
@@ -155,9 +155,9 @@ int V4L2Subdevice::setCompose(unsigned int pad, Rectangle *rect)
  *
  * \return A list of the supported device formats
  */
-ImageFormats V4L2Subdevice::formats(unsigned int pad)
+ImageFormats<unsigned int> V4L2Subdevice::formats(unsigned int pad)
 {
-	ImageFormats formats;
+	ImageFormats<unsigned int> formats;
 
 	if (pad >= entity_->pads().size()) {
 		LOG(V4L2, Error) << "Invalid pad: " << pad;
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index 99470ce11421c77c..f84bd00570afa38c 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -837,9 +837,9 @@ int V4L2VideoDevice::setFormatSingleplane(V4L2DeviceFormat *format)
  *
  * \return A list of the supported video device formats
  */
-ImageFormats V4L2VideoDevice::formats()
+ImageFormats<PixelFormat> V4L2VideoDevice::formats()
 {
-	ImageFormats formats;
+	ImageFormats<PixelFormat> formats;
 
 	for (unsigned int pixelformat : enumPixelformats()) {
 		std::vector<SizeRange> sizes = enumSizes(pixelformat);
diff --git a/test/v4l2_subdevice/list_formats.cpp b/test/v4l2_subdevice/list_formats.cpp
index 067dc5ed30f4edd9..201a294c72a09b4d 100644
--- a/test/v4l2_subdevice/list_formats.cpp
+++ b/test/v4l2_subdevice/list_formats.cpp
@@ -47,7 +47,7 @@ void ListFormatsTest::printFormats(unsigned int pad,
 int ListFormatsTest::run()
 {
 	/* List all formats available on existing "Scaler" pads. */
-	ImageFormats formats;
+	ImageFormats<unsigned int> formats;
 
 	formats = scaler_->formats(0);
 	if (formats.isEmpty()) {
