Image Optimization Workflow

Complete guide for creating, optimizing, and managing images for blog posts.

Table of Contents

  1. Image Types
  2. Creating Images
  3. Optimization Process
  4. Naming Conventions
  5. Storage Structure
  6. Automation Tools

Image Types

  • Purpose: Blog post headers, social media cards
  • Dimensions: 1200x630px (2:1 ratio)
  • File size: < 200KB
  • Format: JPG (or WebP with JPG fallback)

2. Content Images (In-post)

  • Purpose: Screenshots, diagrams, illustrations
  • Max width: 1200px
  • File size: < 150KB per image
  • Format: PNG for screenshots, JPG for photos

3. Thumbnails (Optional)

  • Purpose: Post listings, archives
  • Dimensions: 400x200px
  • File size: < 50KB
  • Format: JPG or WebP

Creating Images

Design Tools

  1. Canva (Recommended for beginners)

    • Use “Blog Banner” template (1200x630)
    • Free tier includes many templates
    • Export as JPG at 80% quality
  2. Figma (Recommended for designers)

    • More control over design
    • Reusable components
    • Export as PNG or JPG
  3. Photopea (Free alternative)

    • Browser-based
    • Similar to Photoshop
    • No account needed

Design Guidelines

Technical Posts:

1
2
3
4
[Background: Dark code editor theme]
+ [Post Title: Large, bold, white text]
+ [Tech logos: GitHub, Docker, etc.]
+ [Optional: Code snippet as texture]

Startup/Business Posts:

1
2
3
4
[Background: Professional gradient or photo]
+ [Post Title: Clean, readable font]
+ [Company logo or icon]
+ [Subtle pattern or shape]

Personal Posts:

1
2
3
4
[Background: Warm, inviting photo]
+ [Post Title: Friendly font]
+ [Personal photo or illustration]
+ [Soft overlay for text readability]

Content Images

Screenshots

  1. Take screenshot at 2x resolution
  2. Crop to relevant area only
  3. Annotate if needed (arrows, highlights)
  4. Optimize before adding to post

Tools:

  • macOS: Cmd+Shift+4
  • Windows: Snipping Tool / Snip & Sketch
  • Linux: Flameshot, GNOME Screenshot

Diagrams

  1. Create in Excalidraw, draw.io, or Mermaid
  2. Export as PNG or SVG
  3. Optimize for web

Tools:

  • Excalidraw - Hand-drawn style
  • draw.io - Professional diagrams
  • Mermaid - Code-based diagrams (can embed in markdown)

Optimization Process

Automated Optimization Pipeline

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 1. Install optimization tools
npm install -g sharp-cli
# or use online tools like tinypng.com, squoosh.app

# 2. Optimize single image
npx sharp -i input.png -o output.jpg --quality 80 --progressive

# 3. Batch optimize all images in a folder
for img in *.{jpg,png}; do
  npx sharp -i "$img" -o "optimized-$img" --quality 85
done

Manual Optimization

  1. Visit squoosh.app
  2. Upload your image
  3. Choose format:
    • JPG for photos (quality: 80-85)
    • WebP for modern browsers (quality: 80)
    • PNG only for images requiring transparency
  4. Compare before/after
  5. Download optimized image

Using TinyPNG

  1. Visit tinypng.com
  2. Upload up to 20 images
  3. Download compressed versions
  4. Average reduction: 60-70%

Optimization Script

Save as scripts/optimize-images.sh:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash

# Image Optimization Script
# Usage: ./optimize-images.sh path/to/images

INPUT_DIR="${1:-.}"
OUTPUT_DIR="${INPUT_DIR}/optimized"

mkdir -p "$OUTPUT_DIR"

echo "Optimizing images in: $INPUT_DIR"

# Optimize JPGs
find "$INPUT_DIR" -maxdepth 1 -type f \( -name "*.jpg" -o -name "*.jpeg" \) | while read img; do
    filename=$(basename "$img")
    echo "Optimizing: $filename"
    convert "$img" -quality 85 -strip "$OUTPUT_DIR/$filename"
done

# Optimize PNGs
find "$INPUT_DIR" -maxdepth 1 -type f -name "*.png" | while read img; do
    filename=$(basename "$img")
    echo "Optimizing: $filename"
    pngquant --quality=80-90 "$img" --output "$OUTPUT_DIR/$filename"
done

echo "Optimization complete! Check: $OUTPUT_DIR"

Quality Guidelines

Image TypeFormatQualityMax Size
Featured ImageJPG80-85200KB
ScreenshotPNG85-90150KB
PhotoJPG80150KB
DiagramPNG/SVG90100KB
IconSVG-10KB

Naming Conventions

1
2
3
featured.jpg              # Default name
featured-dark.jpg         # Dark theme variant
featured-social.jpg       # Social media specific

Content Images

1
2
3
4
diagram-architecture.png  # Descriptive names
screenshot-dashboard.png
photo-team.jpg
icon-github.svg

Naming Rules

  1. Lowercase only
  2. Use hyphens for spaces
  3. Be descriptive but concise
  4. No special characters except hyphens
  5. Include context (what the image shows)

Good:

  • cloudflare-r2-upload-diagram.png
  • github-actions-workflow.jpg
  • kwippy-screenshot-homepage.png

Bad:

  • Screen Shot 2024-11-16 at 3.45.23 PM.png
  • IMG_1234.jpg
  • untitled-1-copy-final-FINAL.png

Storage Structure

1
2
3
4
5
6
content/post/
  └── 2024-10-21-mastering-github-actions-arm/
      ├── index.md
      ├── featured.jpg
      ├── diagram-workflow.png
      └── screenshot-results.png

Pros:

  • All assets organized with post
  • Easy to manage
  • Clear ownership

Cons:

  • Requires folder for each post
  • Can’t easily share images across posts

Option 2: Flat Structure with Central Images (Current approach)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
content/post/
  ├── 2024-10-21-mastering-github-actions-arm.md
  └── ...

static/images/
  ├── featured/
  │   ├── 2024-10-21-github-actions-arm.jpg
  │   └── ...
  └── content/
      ├── diagram-workflow.png
      └── screenshot-results.png

Pros:

  • Simple file structure
  • Easy to reference across posts
  • Centralized image management

Cons:

  • Harder to track which images belong to which posts
  • Can get crowded
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
content/post/
  ├── simple-post.md                    # Text-only posts
  ├── post-with-images/                 # Posts with images
  │   ├── index.md
  │   ├── featured.jpg
  │   └── diagram.png
  └── ...

static/images/shared/                   # Shared resources
  ├── logos/
  ├── icons/
  └── banners/

Automation Tools

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// generate-featured-image.js
// Generates consistent featured images from templates

const { createCanvas, loadImage } = require('canvas');
const fs = require('fs');

async function generateFeaturedImage(title, category, outputPath) {
  const width = 1200;
  const height = 630;
  const canvas = createCanvas(width, height);
  const ctx = canvas.getContext('2d');

  // Background gradient
  const gradient = ctx.createLinearGradient(0, 0, width, height);
  gradient.addColorStop(0, '#667eea');
  gradient.addColorStop(1, '#764ba2');
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, width, height);

  // Title text
  ctx.fillStyle = '#ffffff';
  ctx.font = 'bold 60px Arial';
  ctx.textAlign = 'center';
  ctx.fillText(title, width / 2, height / 2);

  // Save
  const buffer = canvas.toBuffer('image/jpeg', { quality: 0.85 });
  fs.writeFileSync(outputPath, buffer);
}

// Usage
generateFeaturedImage(
  'Your Post Title',
  'Technology',
  'content/post/your-post/featured.jpg'
);

2. Bulk Image Optimizer

Create scripts/bulk-optimize.sh:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash

# Find all images larger than 200KB and optimize them
find content/post -type f \( -name "*.jpg" -o -name "*.png" \) -size +200k | while read img; do
    echo "Large image found: $img"

    # Backup original
    cp "$img" "$img.backup"

    # Optimize
    if [[ $img == *.jpg ]]; then
        convert "$img" -quality 85 -strip "$img.optimized"
    else
        pngquant --quality=85 "$img" --output "$img.optimized"
    fi

    # Replace if smaller
    if [ -f "$img.optimized" ]; then
        mv "$img.optimized" "$img"
        echo "Optimized: $img"
    fi
done

3. WebP Conversion

1
2
3
4
5
6
7
8
#!/bin/bash

# Convert all JPGs to WebP for modern browsers
find content/post -name "*.jpg" | while read img; do
    webp_img="${img%.jpg}.webp"
    cwebp -q 85 "$img" -o "$webp_img"
    echo "Created: $webp_img"
done

4. Pre-commit Hook

Add to .git/hooks/pre-commit:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash

# Check for large images before commit
large_images=$(find content/post -type f \( -name "*.jpg" -o -name "*.png" \) -size +300k)

if [ ! -z "$large_images" ]; then
    echo "Error: Large images detected (>300KB):"
    echo "$large_images"
    echo ""
    echo "Please optimize before committing:"
    echo "  ./scripts/optimize-images.sh"
    exit 1
fi

Best Practices Checklist

Before Publishing

  • Featured image is 1200x630px
  • All images are optimized (< 200KB)
  • Images have descriptive file names
  • Alt text added for accessibility
  • Images are referenced correctly in markdown
  • Images load correctly in preview

Accessibility

Always include alt text:

1
![GitHub Actions workflow diagram showing build, test, and deploy stages](diagram-workflow.png)

Good alt text:

  • Descriptive
  • Concise
  • Mentions important details
  • Doesn’t start with “Image of…”

Examples:

  • ✅ “GitHub Actions workflow diagram showing build, test, and deploy stages”
  • ✅ “Cloudflare R2 dashboard showing uploaded files”
  • ❌ “Image”
  • ❌ “Screenshot”

Quick Reference

  1. Open Canva/Figma
  2. Create 1200x630px canvas
  3. Add title, logo, background
  4. Export as JPG, 80% quality
  5. Optimize at squoosh.app if needed
  6. Save as featured.jpg

Optimize Existing Image

1
2
3
4
5
6
7
8
# Using online tool
1. Visit squoosh.app
2. Upload image
3. Set quality to 80-85
4. Download

# Using command line
convert input.jpg -quality 85 -strip output.jpg

Add to Post

1
2
3
![Description](featured.jpg)
# or with folder structure
![Description](./featured.jpg)

Last Updated: 2024-11-16 Tools Required: Canva/Figma, Squoosh/TinyPNG, ImageMagick (optional) Estimated Time: 10-15 minutes per featured image

Last updated on Nov 16, 2025 22:41 UTC
Writing about the internet