Skip to content

Sanity Hotspot Implementation Documentation

Overview

This document describes the implementation of predictable image cropping using Sanity hotspots in the Feyenoord frontend. The solution ensures that images cropped and hotspotted in Sanity Studio are displayed consistently in the frontend by maintaining precise aspect ratios across different viewport sizes.

Problem Statement

Sanity's image hotspot functionality allows content editors to define which part of an image should remain visible when cropping occurs. However, unpredictable cropping can happen when:

  • Only the width is passed to Sanity's imageBuilder without specifying height
  • Sanity defaults to using the original image's width and height (aspect ratio) instead of the frontend's display width and height (aspect ratio)
  • Large differences exist between the original image's aspect ratio and the displayed aspect ratio in the frontend

This leads to unexpected cropping behavior where the hotspot-defined area may not be visible as intended.

Solution Architecture

Fixed Aspect Ratios

To ensure predictable cropping and hotspot implementation, we implement fixed aspect ratios for all image components. Each component defines specific aspect ratios for different viewports:

  • Mobile/Tablet
  • Desktop

Dual Image Rendering

Components render both mobile and desktop versions of images simultaneously, showing/hiding them with CSS media queries. This approach:

  • Prevents layout shift: Ensures images don't jump when a different aspect ratio is loaded on the server versus client
  • Enables dual aspect ratios: Allows passing separate aspect ratios for mobile/tablet and desktop, ensuring we always know the correct height based on width, thus properly respecting Sanity hotspots and cropping
  • Maintains consistent aspect ratios across viewports
  • Ensures hotspot cropping matches frontend display

Dynamic Height Calculation

The getResponsiveImage function dynamically calculates image heights based on:

  • Dynamic width provided by Next.js Image loader
  • Specified aspect ratios
  • Maintains image optimization through sizes prop with loader

Implementation Details

Core Functions

getResponsiveImage

Located in /apps/web/lib/sanity/image.ts, this function creates responsive images with fixed aspect ratios that properly respect Sanity hotspots and cropping:

typescript
export function getResponsiveImage({
	image,
	priority = false,
	aspectRatio, // e.g., "16/9", "4/3", "2/1"
	sizes, // Responsive sizes string
}: GetResponsiveImageProps): FieldImage | undefined;

Key features:

  • Uses Next.js Image loader for dynamic width handling
  • Calculates height using getImageHeightWithAspectRatio(width, aspectRatio)
  • Passes both width and height to Sanity's imageBuilder
  • Generates optimized srcSet for different screen sizes

getImageHeightWithAspectRatio

Helper function in /apps/web/lib/sanity/image.helpers.ts that calculates height from width and aspect ratio:

typescript
export function getImageHeightWithAspectRatio(
	width: number,
	aspectRatio: AspectRatio, // e.g., "16/9"
): number {
	const [aspectWidth, aspectHeight] = aspectRatio.split("/").map(Number);
	return Math.round((width / aspectWidth) * aspectHeight);
}

Aspect Ratio Types

Predefined aspect ratios available in /apps/web/lib/sanity/image.helpers.ts:

typescript
export type AspectRatio =
	| "1/1" // Square
	| "3/2" // Landscape
	| "3/4" // Portrait
	| "4/3" // Standard
	| "9/16" // Vertical video
	| "16/9" // Widescreen
	| "1/2" // Half height
	| "2/1"; // Double width

Component Implementation Examples

CampaignHero Component (/apps/web/components/images/CampaignHeroImage.tsx)

typescript
const imageWithHotspot = useMemo(() => {
	const BASE_IMAGE_CONFIG = {
		fallbackImage: { src: "", alt: "" },
		priority: true,
	};

	const mobileImage = getResponsiveImage({
		image: imageMobile ?? image,
		currentAspectRatio: ASPECT_RATIO.mobile,
		sizes: "(max-width: 768px) 100vw, 768px",
		...BASE_IMAGE_CONFIG,
	});

	const desktopImage = getResponsiveImage({
		image,
		currentAspectRatio: ASPECT_RATIO.tablet,
		sizes: "(max-width: 1440px) 100vw, 1440px",
		...BASE_IMAGE_CONFIG,
	});

	return {
		mobileImage,
		image: desktopImage,
	};
}, [image, imageMobile]);

Benefits

Predictable Cropping

  • Hotspots defined in Sanity Studio translate directly to frontend display
  • No unexpected cropping due to aspect ratio mismatches
  • Consistent behavior across all devices and screen sizes

Performance Optimization

  • Images remain optimized through Next.js Image component
  • Proper srcSet generation for different viewport sizes
  • Maintains lazy loading and priority loading capabilities

Maintainable Architecture

  • Centralized aspect ratio definitions
  • Consistent implementation pattern across components
  • Type-safe aspect ratio handling

Usage Guidelines

For New Components

  1. Choose appropriate aspect ratios based on design requirements
  2. Use getResponsiveImage function for image processing
  3. Define separate mobile and desktop aspect ratios when needed
  4. Pass correct sizes attribute for responsive behavior
  5. Render both mobile and desktop images with CSS media query visibility

Media Query Implementation

Components should handle image visibility through CSS:

css
/* Mobile image visible by default */
.mobile {
	display: block;
}
.desktop {
	display: none;
}

/* Desktop breakpoint */
@media (min-width: 769px) {
	.mobile {
		display: none;
	}
	.desktop {
		display: block;
	}
}

Technical Considerations

Image Optimization

  • Next.js Image component handles lazy loading automatically
  • srcSet generation includes retina display support
  • Quality settings (75) balance file size and visual quality

Responsive Behavior

  • sizes attribute ensures correct image selection for different viewports
  • Dynamic width calculation maintains aspect ratio consistency
  • Fallback to original aspect ratio only when no specific ratio is defined

Sanity Integration

  • Works with existing Sanity hotspot functionality
  • No changes required to content schema
  • Compatible with all Sanity image field configurations

Future Enhancements

Potential improvements to consider:

  1. Dynamic aspect ratio detection for components with variable layouts
  2. Additional aspect ratio presets based on emerging design patterns