const sharp = require('sharp'); async function run() { try { // Trim transparency to get the exact bounding box of the logo const trimmed = sharp('public/assets/logo.png').trim(); const { info, data } = await trimmed.toBuffer({ resolveWithObject: true }); // Now data is the cropped image. // The icon is usually the left-most part. It's often square. Let's assume the height of the trimmed image is the width of the icon. const iconSize = info.height; // Extract the left square await sharp(data) .extract({ left: 0, top: 0, width: iconSize, height: iconSize }) // Resize to a standard icon size, like 192x192 .resize(192, 192, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }) .toFile('public/icon.png'); // Create an apple-icon as well just in case await sharp(data) .extract({ left: 0, top: 0, width: iconSize, height: iconSize }) .resize(180, 180, { fit: 'contain', background: { r: 255, g: 255, b: 255, alpha: 1 } }) .toFile('public/apple-icon.png'); // Convert to .ico as well to replace the 0-byte favicon.ico // sharp doesn't natively support .ico but we can just use icon.png and next.js handles it. // However, I will delete the empty favicon.ico so Next.js falls back to icon.png const fs = require('fs'); if (fs.existsSync('public/favicon.ico')) { fs.unlinkSync('public/favicon.ico'); } if (fs.existsSync('app/favicon.ico')) { fs.unlinkSync('app/favicon.ico'); } console.log('Successfully generated public/icon.png and public/apple-icon.png'); } catch (err) { console.error(err); } } run();