Claude Skills Guide

Building a Chrome extension for social media image resizing solves a real problem. Every platform demands different dimensions—Instagram posts need 1080×1080, Twitter/X headers require 1500×500, LinkedIn banners want 1584×396, and Facebook cover photos need 820×312. Manually adjusting images for each platform wastes time. A well-built extension automates this workflow entirely.

This guide covers the architecture, implementation patterns, and key decisions for creating a production-ready social media image resizer extension.

Extension Architecture

A Chrome extension consists of three core components: the manifest file, background scripts, and content scripts or popup interfaces. For image resizing, you’ll need:

  1. Manifest V3 configuration with appropriate permissions
  2. Canvas API for image manipulation
  3. File handling APIs for saving resized images
  4. Storage for user preferences and presets

The manifest defines what your extension can access:

{
  "manifest_version": 3,
  "name": "Social Media Image Resizer",
  "version": "1.0.0",
  "permissions": ["storage", "downloads", "activeTab"],
  "action": {
    "default_popup": "popup.html",
    "default_icon": "icon.png"
  },
  "host_permissions": ["<all_urls>"]
}

Core Resizing Logic

The Canvas API provides the foundation for image manipulation. The resizing function accepts source image data and target dimensions, then outputs a resized blob:

async function resizeImage(imageSource, targetWidth, targetHeight, format = 'image/png') {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    
    img.onload = () => {
      const canvas = document.createElement('canvas');
      canvas.width = targetWidth;
      canvas.height = targetHeight;
      
      const ctx = canvas.getContext('2d');
      ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
      
      canvas.toBlob((blob) => {
        if (blob) resolve(blob);
        else reject(new Error('Failed to create blob'));
      }, format, 0.92);
    };
    
    img.onerror = () => reject(new Error('Failed to load image'));
    img.src = imageSource;
  });
}

This function handles the core transformation. The 0.92 quality parameter balances file size against visual fidelity for JPEG output.

Platform Presets

Different platforms enforce strict dimension requirements. Store these as configurable presets:

const PLATFORM_PRESETS = {
  instagram: {
    post: { width: 1080, height: 1080, label: 'Instagram Square' },
    portrait: { width: 1080, height: 1350, label: 'Instagram Portrait' },
    story: { width: 1080, height: 1920, label: 'Instagram Story' }
  },
  twitter: {
    header: { width: 1500, height: 500, label: 'X/Twitter Header' },
    post: { width: 1200, height: 675, label: 'X/Twitter Image' }
  },
  linkedin: {
    banner: { width: 1584, height: 396, label: 'LinkedIn Banner' },
    post: { width: 1200, height: 627, label: 'LinkedIn Post' }
  },
  facebook: {
    cover: { width: 820, height: 312, label: 'Facebook Cover' },
    post: { width: 1200, height: 630, label: 'Facebook Post' }
  }
};

Users select a preset, and the extension applies the corresponding dimensions automatically.

Integration Approaches

You have three primary integration strategies:

1. Popup Interface

The popup provides a quick-access interface visible in the Chrome toolbar. This works well for one-click operations:

// popup.js
document.getElementById('resizeBtn').addEventListener('click', async () => {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  
  chrome.tabs.sendMessage(tab.id, { action: 'captureImage' }, async (imageData) => {
    const preset = document.getElementById('presetSelect').value;
    const { width, height } = JSON.parse(preset);
    
    const resized = await resizeImage(imageData, width, height);
    await chrome.downloads.download({
      url: URL.createObjectURL(resized),
      filename: `resized-${width}x${height}.png`
    });
  });
});

2. Context Menu Integration

Right-click context menus provide alternative access:

chrome.contextMenus.create({
  id: 'resizeImage',
  title: 'Resize for Social Media',
  contexts: ['image']
});

chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === 'resizeImage') {
    chrome.tabs.sendMessage(tab.id, { 
      action: 'resizeFromContext',
      imageUrl: info.srcUrl 
    });
  }
});

3. Drag-and-Drop Zone

For the popup or options page, a drag-and-drop interface lets users upload images directly:

const dropZone = document.getElementById('dropZone');

dropZone.addEventListener('drop', async (e) => {
  e.preventDefault();
  const file = e.dataTransfer.files[0];
  
  if (file && file.type.startsWith('image/')) {
    const reader = new FileReader();
    reader.onload = (event) => processImage(event.target.result);
    reader.readAsDataURL(file);
  }
});

Image Processing Pipeline

The complete processing pipeline involves several stages:

  1. Input capture — Get the source image from the page, drag-drop, or clipboard
  2. Validation — Verify image dimensions and format compatibility
  3. Transformation — Resize using canvas with appropriate scaling algorithms
  4. Output generation — Create blob in desired format (PNG, JPEG, WebP)
  5. Download — Trigger browser download with appropriate filename

For best results, implement smart cropping when aspect ratios don’t match:

async function smartCrop(imageSource, targetWidth, targetHeight) {
  const img = await loadImage(imageSource);
  const sourceRatio = img.width / img.height;
  const targetRatio = targetWidth / targetHeight;
  
  let sx, sy, sw, sh;
  
  if (sourceRatio > targetRatio) {
    // Source is wider - crop sides
    sh = img.height;
    sw = img.height * targetRatio;
    sy = 0;
    sx = (img.width - sw) / 2;
  } else {
    // Source is taller - crop top/bottom
    sw = img.width;
    sh = img.width / targetRatio;
    sx = 0;
    sy = (img.height - sh) / 2;
  }
  
  return cropAndResize(img, sx, sy, sw, sh, targetWidth, targetHeight);
}

Handling Cross-Origin Images

When processing images from websites, CORS restrictions apply. The extension needs appropriate permissions and the crossOrigin attribute set:

async function loadImageWithCors(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.onload = () => resolve(img);
    img.onerror = reject;
    img.src = url;
  });
}

The website serving the image must include Access-Control-Allow-Origin headers, or you need to route the image through a proxy service in your background script.

User Preferences Storage

Persist user preferences using Chrome’s storage API:

async function savePreferences(prefs) {
  await chrome.storage.local.set({ userPreferences: prefs });
}

async function loadPreferences() {
  const result = await chrome.storage.local.get('userPreferences');
  return result.userPreferences || { defaultFormat: 'png', defaultQuality: 0.92 };
}

This allows users to set their preferred output format, default platform, and quality settings across sessions.

Performance Considerations

Image processing in the browser can be memory-intensive. Optimize by:

Testing and Debugging

Use Chrome’s developer tools to debug extension components:

Test with various image sizes and formats, including edge cases like extremely wide or tall images, to ensure graceful handling.

A well-designed social media image resizer extension eliminates repetitive manual work. The patterns outlined here provide a foundation for building extensions that integrate smoothly with users’ existing workflows while handling the diverse requirements of modern social platforms.

Built by theluckystrike — More at zovo.one