agentsclimarketplace

Seo images

Skill PIXARTSeu/Synapse/packages/codegraph/data/skill/seo-images

Self-improving AI brain for Claude Code & Desktop — 28 MCP tools, 253 skills, collective memory, project tracking, work logs. One server, all your sessions share the same knowledge. Deploy on Coolify in 2 minutes.

Install
npx -y skills add PIXARTSeu/Synapse --skill seo-images

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 8 stars8 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Image SEO and performance for Next.js. Covers alt text, descriptive filenames, next/image responsive output, AVIF/WebP, lazy loading and LCP priority, CLS prevention, IPTC/XMP metadata, AI-generated image labeling (DigitalSourceType TrainedAlgorithmicMedia), OG/social images, and image sitemaps. Use when working on image optimization, alt text, image audit, or image SEO. Triggers on: image SEO, alt text, next/image, image optimization, image audit, WebP, AVIF, image metadata, OG image, image sitemap.

SKILL.md

12.7 KB, ~3.2k tokens by cl100k_base, as published. Nobody here has run it

Image SEO & Optimization

Two modes: implement (write correct image markup/config in our Next.js stack) and audit (score an existing site against the rubric below and tell the team what to fix first). Both are covered here.


Part 1 — Implement (Next.js 15 + Tailwind)

next/image is the default

next/image gives us responsive srcset, lazy loading, async decoding, blur placeholders, and CLS-safe dimensions for free. Prefer it over raw <img> for any content/hero image. Reach for raw <img> only for cases the component can't serve well (e.g. inline SVG, or a CMS HTML blob you don't control).

import Image from "next/image";

// Above-the-fold / LCP image: priority loads eagerly + sets fetchpriority="high".
// Never use loading="lazy" here — it directly harms LCP.
<Image
  src="/blue-running-shoes.jpg"
  alt="Blue mesh running shoes on a wooden floor"
  width={1200}
  height={630}
  priority
  sizes="(max-width: 768px) 100vw, 1200px"
/>

// Below-the-fold image: lazy by default, give real intrinsic dimensions for CLS.
<Image
  src="/team-meeting.jpg"
  alt="Team meeting in a modern office conference room"
  width={800}
  height={600}
  sizes="(max-width: 768px) 100vw, 800px"
/>

Rules that matter:

  • One priority image per route — the LCP element. More than one defeats the purpose (everything competes for bandwidth).
  • Always pass width/height (or fill + a sized parent) so the box is reserved before load → no layout shift.
  • sizes must reflect the real rendered width at each breakpoint, or Next ships an oversized variant. Match it to your Tailwind layout.
  • For a layout-driven image (card, full-bleed hero) use fill:
<div className="relative aspect-[16/9]">
  <Image src="/hero.jpg" alt="..." fill priority
         sizes="100vw" className="object-cover" />
</div>

Format & quality config

Configure modern formats globally; Next negotiates AVIF → WebP → original per Accept header.

// next.config.ts
const nextConfig = {
  images: {
    formats: ["image/avif", "image/webp"], // AVIF first = best compression
    // qualities: [50, 75, 90],            // Next 16: whitelist allowed quality values
    remotePatterns: [
      { protocol: "https", hostname: "cms.pixarts.eu" }, // Payload media
    ],
  },
};
export default nextConfig;

Format priority and current support: AVIF (~93%) best compression, WebP (~96%) safe default, JPEG/PNG fallback handled by the browser via negotiation. (JPEG XL is feature-complete in Chromium behind a Rust decoder but not yet in stable — monitor, don't deploy.)

Payload CMS media

For CMS-driven images, generate sizes in the Media collection so the front end gets right-sized derivatives and next/image doesn't upscale:

// Media collection (Payload)
imageSizes: [
  { name: "thumb", width: 400 },
  { name: "card",  width: 800 },
  { name: "hero",  width: 1600 },
],
formatOptions: { format: "webp", options: { quality: 82 } },

Always require alt on the Media collection (required: true) so editors can't upload an image with no alt text. Map Payload's stored alt straight into the <Image alt={...}> prop — never fall back to the filename.

Alt text (this is the #1 image ranking signal)

  • Present on every meaningful image. Decorative-only images get alt="" (empty, not absent) so screen readers skip them.
  • Describe the content, not the file: "Blue mesh running shoes on a wooden floor", not "image.jpg".
  • Natural keywords where they fit. Never stuff: "shoes shoes running shoes" is a quality flag, not a boost.
  • Length ~10–125 chars. Longer than that usually means it's a caption, not alt.
GoodBadWhy
"Professional plumber repairing a kitchen sink faucet""image.jpg"filename, not description
"Red 2024 Toyota Camry sedan, front three-quarter view""plumber plumbing plumber services"keyword stuffing
"" (decorative divider)"Click here"non-descriptive

Filenames

Descriptive, lowercase, hyphenated, keyworded: blue-running-shoes.webp, not IMG_1234.jpg. The filename is a real Google Images signal. For Payload uploads, slugify the original filename on upload rather than keeping camera names.

LCP, lazy loading, CLS — the perf triad

  • priority on the LCP image; everything else lazy by default (next/image handles this).
  • Reserve space (width/height or fill + aspect ratio) → CLS < 0.1.
  • Don't lazy-load above-the-fold. Don't eager-load below-the-fold.

If you must drop to raw <img> (uncontrolled HTML), the manual equivalents are fetchpriority="high" for LCP, loading="lazy" + decoding="async" below the fold, and explicit width/height:

<img src="/hero.webp" alt="..." width="1200" height="630" fetchpriority="high">
<img src="/photo.webp" alt="..." width="600" height="400" loading="lazy" decoding="async">

OG / social preview image

Each route needs an og:image for link previews (a soft SEO/CTR signal, not a ranking factor). Generate it dynamically with the App Router:

// app/[locale]/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from "next/og";
export const size = { width: 1200, height: 630 }; // min 1200x630, 1.91:1
export const contentType = "image/png";

export default async function Image({ params }) {
  const post = await getPost(params.slug);
  return new ImageResponse(
    <div style={{ display: "flex", /* ...brand layout... */ }}>{post.title}</div>,
    { ...size },
  );
}

Image sitemap

Google discovers images through page crawl, but an explicit <image:image> extension in the sitemap helps for image-heavy/gallery pages. Emit it from app/sitemap.ts when a route has notable imagery:

// app/sitemap.ts — add image entries to relevant routes
return posts.map((p) => ({
  url: `${base}/blog/${p.slug}`,
  lastModified: p.updatedAt,
  images: [`${base}${p.heroImage.url}`], // Next emits <image:image> for these
}));

IPTC / XMP metadata & AI-generated labeling

File-embedded metadata (use your own image tooling / CLI to read+write EXIF / IPTC / XMP):

  • IPTC Creator / Credit / Copyright → Google Images shows these in the rich-result panel. Display + attribution only, NOT a ranking factor. Worth setting for brand attribution; not worth obsessing over.
  • EXIF camera data, IPTC keywords → ignored by Google for SEO. Don't rely on them.
  • WebP supports EXIF + XMP but not IPTC natively — write the equivalent XMP fields for WebP assets (most metadata tools handle the IPTC→XMP mapping).

AI-generated images — operational requirement, not ranking. For generative-AI imagery (especially product images in a Merchant Center / shopping feed), Google requires the IPTC DigitalSourceType label. Missing it can get a product feed disapproved — so treat it as a compliance step, not an optimization.

Set the XMP-iptcExt DigitalSourceType to the matching IPTC vocabulary URI:

Value (URI suffix on cv.iptc.org/newscodes/digitalsourcetype/...)Use for
trainedAlgorithmicMediaFully AI-generated (diffusion-model product/hero imagery)
compositeSyntheticMix of captured + AI-generated elements
digitalCaptureFully captured photograph, no AI element

When optimizing AI-generated assets, confirm the source type with the user and embed the matching value. Separately, AI-generated product titles/descriptions must be labeled at the feed layer (Merchant Center), not the page — flag that to whoever owns the product feed.


Part 2 — Audit (methodology + scoring)

When auditing an existing site (use your own crawler/headless tooling to fetch rendered HTML and image responses), classify and score.

Detect the lazy-loading mechanism before flagging

A missing native loading="lazy" is not a regression if a JS lazy-loader is in use. Classify each image's mechanism, then report it alongside loading:

MechanismSignalNote
nativeloading="lazy" attributeModern default
js-genericdata-src / data-srcset / data-original, or class lazyload/lazyLazysizes, vanilla-lazyload, etc. — native attr intentionally absent
pluginvendor-specific data-* attrs / classesWordPress optimizer plugins, etc.
noneno signalGenuinely not lazy-loaded

Thresholds

File size by category:

CategoryTargetWarningCritical
Thumbnails< 50 KB> 100 KB> 200 KB
Content images< 100 KB> 200 KB> 500 KB
Hero / banner< 200 KB> 300 KB> 700 KB

CWV gates the image touches: LCP < 2.5s, CLS < 0.1 (INP < 200ms is mostly JS, but oversized images compete for the main thread on decode).

0–100 scoring rubric

Start at 100, subtract per issue, floor at 0.

CheckDeduction
<img>/<Image> missing meaningful alt (per image, cap −25)−5 each
Alt present but non-descriptive or keyword-stuffed (cap −10)−2 each
LCP image lazy-loaded (or not priority)−15
Image with no reserved dimensions → CLS risk (cap −15)−3 each
Oversized image at Critical threshold (cap −20)−5 each
No modern format offered (AVIF/WebP) where photos served−10
Generic/camera filenames at scale−5
Missing og:image on a shareable route−5
AI-generated product image missing DigitalSourceType (feed compliance)−10

Bands: 90–100 excellent · 75–89 good · 50–74 needs work · <50 failing.

What actually moves Google Images

FactorImpactWhere set
Alt textCRITICAL (ranking)<Image alt> / <img alt>
FilenameHIGH (ranking)file system, slugified
Surrounding page contextHIGH (ranking)nearby copy, heading, caption
File size / speedMEDIUM (indirect via CWV)format + compression
IPTC Creator/CopyrightLOW (display only)embedded metadata
EXIF camera data, IPTC keywordsNONEignore for SEO

Falsifiability — how would we know this failed?

Don't declare an image pass "done" without a measurable check:

  • Claim: "Images are optimized for LCP." → Falsify: field LCP for the page is still > 2.5s on mobile after deploy (check CrUX / RUM, not just lab).
  • Claim: "Alt text is complete." → Falsify: a crawl still returns meaningful <img> with empty/missing alt, OR Search Console Image indexing doesn't rise over ~4 weeks.
  • Claim: "Modern formats shipped." → Falsify: response Content-Type is still image/jpeg/png for content images when the client sent Accept: image/avif.
  • Leading indicator: total image bytes per page and count of dimensionless images both trend down in the next crawl. If they don't, the change didn't land.

Audit output template

Summary

MetricStatusCount
Total imagesXX
Missing altfailXX
Oversized (Critical)warnXX
Legacy format onlywarnXX
No dimensions (CLS risk)warnXX
LCP image lazy-loadedfail0/1
ScoreNN/100

Prioritized fix list — sort by byte savings × traffic, largest first:

ImageCurrentFormatIssuesEst. savingsFix
/hero.jpg640 KBJPEGoversized, lazy LCP, no AVIF~480 KBpriority, AVIF, compress

Recommendations — concrete, ordered: convert N images to AVIF/WebP (~XX KB), add alt to N images, reserve dimensions on N, set priority on the LCP image, slugify N camera filenames, add og:image to N routes.

Error handling

ScenarioAction
URL unreachableReport status code; suggest checking auth/availability
No <img>/<Image> foundNote images may be CSS background-image or JS-injected; re-check rendered DOM
Images behind CDN/authReport markup-derived data (alt, dims, format) and flag size as inaccessible
Metadata tooling unavailableReport HTML-level findings; note embedded metadata couldn't be inspected

Parts adapted from claude-seo (MIT, © 2026 agricidaniel).

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.