@ai-growth/nextjs
Version:
Seamlessly integrate Sanity CMS with Next.js applications for automated blog routing and rendering
147 lines (146 loc) • 4.2 kB
JavaScript
/**
* @fileoverview Image Processing Utilities for SEO
*
* This module provides functions for processing Sanity images
* and preparing them for SEO meta tags and structured data.
*/
/**
* Process a Sanity image or string URL for SEO use
*
* @param image - Sanity image object or string URL
* @param baseUrl - Base URL for relative paths
* @returns Processed image data for SEO
*/
export function processImage(image, baseUrl) {
if (!image) {
return {
url: `${baseUrl}/default-og-image.jpg`,
alt: 'Default image',
width: 1200,
height: 630,
type: 'image/jpeg',
};
}
// Handle string URLs
if (typeof image === 'string') {
const url = image.startsWith('http') ? image : `${baseUrl}${image}`;
return {
url,
alt: 'Image',
width: 1200,
height: 630,
type: getImageTypeFromUrl(url),
};
}
// Handle Sanity image objects
if (image.asset && image.asset._ref) {
// Extract Sanity image ID and build URL
const imageId = image.asset._ref;
const imageUrl = buildSanityImageUrl(imageId, {
width: 1200,
height: 630,
fit: 'crop',
format: 'jpg',
});
return {
url: imageUrl,
alt: image.alt || 'Image',
width: 1200,
height: 630,
type: 'image/jpeg',
};
}
// Fallback to default image
return {
url: `${baseUrl}/default-og-image.jpg`,
alt: 'Default image',
width: 1200,
height: 630,
type: 'image/jpeg',
};
}
/**
* Build a Sanity image URL with transformations
*
* @param imageRef - Sanity image reference
* @param options - Image transformation options
* @returns Transformed image URL
*/
export function buildSanityImageUrl(imageRef, options = {}) {
// Extract image details from Sanity reference
// Format: image-{assetId}-{width}x{height}-{format}
const parts = imageRef.split('-');
if (parts.length < 2) {
return imageRef; // Return as-is if not a valid Sanity reference
}
const assetId = parts[1];
const extension = parts[parts.length - 1] || 'jpg';
// Build Sanity CDN URL
let url = `https://cdn.sanity.io/images/project/dataset/${assetId}.${extension}`;
// Add transformations
const params = new URLSearchParams();
if (options.width) {
params.append('w', options.width.toString());
}
if (options.height) {
params.append('h', options.height.toString());
}
if (options.fit) {
params.append('fit', options.fit);
}
if (options.format && options.format !== 'auto') {
params.append('fm', options.format);
}
if (options.quality) {
params.append('q', options.quality.toString());
}
const queryString = params.toString();
if (queryString) {
url += `?${queryString}`;
}
return url;
}
/**
* Get image MIME type from URL extension
*
* @param url - Image URL
* @returns MIME type
*/
export function getImageTypeFromUrl(url) {
const extension = url.split('.').pop()?.toLowerCase();
switch (extension) {
case 'jpg':
case 'jpeg':
return 'image/jpeg';
case 'png':
return 'image/png';
case 'webp':
return 'image/webp';
case 'gif':
return 'image/gif';
case 'svg':
return 'image/svg+xml';
default:
return 'image/jpeg';
}
}
/**
* Generate optimized image dimensions for different use cases
*
* @param useCase - The intended use case for the image
* @returns Optimal dimensions
*/
export function getOptimalImageDimensions(useCase) {
switch (useCase) {
case 'og':
return { width: 1200, height: 630 };
case 'twitter':
return { width: 1200, height: 628 };
case 'thumbnail':
return { width: 400, height: 225 };
case 'hero':
return { width: 1920, height: 1080 };
default:
return { width: 1200, height: 630 };
}
}