ScreenshotNeo

BlogGuides

Optimize Images in Headless WordPress with WPGraphQL

Build an image workflow for headless WordPress: generate useful media sizes, query attachment data with WPGraphQL, and render responsive images in your frontend.

By the ScreenshotNeo team4 October 20269 min read

To optimize images in headless WordPress with WPGraphQL, handle the work in three layers: generate useful image sizes in WordPress, query the attachment data your frontend needs, then render the right asset for each layout. WPGraphQL exposes WordPress media items; querying them does not itself resize or compress images, create responsive HTML, or negotiate a better format.

This guide uses Next.js for a concrete rendering example. The WordPress and GraphQL steps apply more broadly, but use your framework’s own image component or loader if you are not using Next.js.

1. Generate image sizes that match your layouts

WordPress creates intermediate image sizes when media is uploaded. Since WordPress 4.4, its responsive image support can include srcset and sizes in generated image markup, allowing the browser to choose an appropriate candidate for the viewport and display density. A headless frontend does not automatically receive that markup just because it queries an attachment through WPGraphQL.

Start with the slots your site actually uses: for example, a small card thumbnail, a content-width image, and a large hero. Avoid creating many nearly identical widths without a layout reason; each generated size consumes storage and adds processing work during upload.

WordPress provides helpers such as wp_get_attachment_image_srcset() and filters including wp_calculate_image_srcset and wp_calculate_image_sizes for customizing responsive image data. Its documented default sizes behavior may not match your frontend’s CSS, so check the output against the real rendered layout.

Choose an image format deliberately

WordPress supports WebP, and its handbook describes WebP images as around 30% smaller on average than JPEG or PNG equivalents. That is a general statement in the handbook, not a guaranteed saving for a particular site or image. Measure your own assets and inspect visual quality, transparency, animation requirements, and client compatibility.

Sub-sizes normally use the source image format unless output format handling is customized. Decide whether conversion belongs in WordPress’s upload pipeline or in a frontend or CDN delivery layer. WordPress 7.1 documentation also describes browser-side media processing for supported browsers, with a server-side fallback; verify the installed WordPress version, browser support, and hosting behavior before relying on it.

2. Query media data through WPGraphQL

WPGraphQL models WordPress attachments as Media Items. Query the URL and the metadata your frontend needs, such as alternative text and dimensions when those fields are available in your schema. Field names and types can vary with the installed schema and extensions, so inspect the target site’s GraphiQL explorer or schema before adopting a query.

query FindMediaItem($id: ID!) {
  mediaItem(id: $id, idType: DATABASE_ID) {
    sourceUrl
    altText
    mediaDetails {
      width
      height
    }
  }
}

This is an example query shape, not a promise that every WPGraphQL installation exposes these exact fields or types. If it fails validation, inspect the schema for the media item lookup field, the identifier type, and the available URL, alt text, and dimensions fields, then adjust the query. Do not infer that a returned source URL is already a responsive set.

Keep GraphQL responsible for supplying content data. The frontend or image delivery layer chooses how to use that data, including whether to request a specific generated WordPress size, construct responsive candidates, or use a transformation service.

3. Render responsive images in the frontend

For any frontend, reserve space for the image to reduce layout movement, provide meaningful alternative text, and avoid downloading a full-size original for a small card. Choose a source size that is large enough for the rendered slot and its display density. If WordPress provides several suitable candidates, expose them as responsive candidates and give the browser a sizes value that reflects the actual CSS layout.

Next.js example

With Next.js’s default image optimization flow, remote WordPress image URLs must match images.remotePatterns. Keep the host and path pattern as narrow as your media setup permits. Remote images need dimensions because Next.js cannot inspect them at build time; use fill when the containing box determines the dimensions. Give responsive images an accurate sizes value so the browser can choose among source candidates appropriately.

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'cms.example.com',
        pathname: '/wp-content/uploads/**',
      },
    ],
  },
};

module.exports = nextConfig;
// Example component using a URL and metadata returned by your GraphQL query
import Image from 'next/image';

export function ArticleImage({ media }) {
  return (
    <Image
      src={media.sourceUrl}
      alt={media.altText ?? ''}
      width={media.mediaDetails.width}
      height={media.mediaDetails.height}
      sizes="(max-width: 700px) 100vw, (max-width: 1100px) 80vw, 1100px"
    />
  );
}

Replace the example host, upload path, and sizes expression with the real origin and rendered layout. If your query returns null dimensions or your schema does not expose the example nested fields, retrieve dimensions using fields available in that schema or use a layout whose box controls sizing. Do not pass missing values as if they were valid dimensions.

Next.js’s default optimization route does not forward headers when it fetches the remote image. If the media origin requires authentication, the default route may fail; the Next.js documentation suggests considering unoptimized for authenticated sources. For a non-Next.js frontend, follow that framework’s loader and remote-source rules.

4. Choose where image transformations happen

There is no universally best place to resize or convert images. Choose based on the frontend, host capabilities, access controls, and how many variants your layouts need.

Approach Good fit when Check
WordPress upload processing You want generated sizes available from the media origin for many frontend consumers. Confirm the requested sizes are generated for relevant uploads, storage is acceptable, and format output behaves as intended.
Frontend image optimization Your framework can optimize remote media and you want rendering integrated with frontend components. Check remote host configuration, source accessibility, dimensions, sizes, and caching behavior.
External image delivery layer You want transformations or delivery handled outside WordPress and the frontend. Compare host support, operational complexity, origin access, format behavior, and ownership of generated variants.

Compare responsive strategies as well: WordPress-generated sizes and srcset versus variants generated by the frontend or delivery layer. The useful set is the one that fits real component widths without routinely serving oversized originals. For format handling, check compatibility, image quality, transparency or animation needs, and whether the final client actually receives the intended format.

5. Verify the result on real pages

  1. List the image slots and approximate rendered widths used by your templates.
  2. Confirm WordPress generates suitable intermediate sizes for new uploads; regenerate existing media if your workflow requires newly added sizes.
  3. Inspect the deployed WPGraphQL schema and query only fields it actually exposes.
  4. Render the image with a source appropriate to its slot, reserved dimensions, useful alternative text, and an accurate responsive size rule.
  5. Check the browser’s selected image candidate and transferred asset on narrow and wide layouts, including a high-density display if relevant.
  6. Inspect converted images at normal viewing size and verify that transparency, animation, and visual detail survive the chosen format path.

For a screenshot of a public page during visual review, ScreenshotNeo can capture the rendered page as an image or PDF. A screenshot documents what rendered at capture time; it does not measure the image’s transfer size or prove that a responsive candidate was optimal.

6. Troubleshooting

Symptom Likely cause Fix
GraphQL query reports an unknown field The deployed schema differs from the example, or an extension that adds a field is absent. Inspect the schema or GraphiQL explorer and change the query to fields available on that installation.
Remote image is rejected by Next.js The source host, protocol, or path does not match remotePatterns. Configure a specific pattern for the actual media origin and upload path, then verify the URL against it.
Image component has no dimensions The query did not return dimensions, or the media item has incomplete metadata. Query available dimension fields and handle missing values. Use a suitable fill layout if the container controls sizing.
Browser downloads a larger file than expected The original URL is being used, the responsive candidates are missing, or sizes does not reflect the layout. Inspect the rendered source candidates and selected request; align requested widths and sizes with the actual CSS slot.
Images look soft or show artifacts The chosen derivative is too small or conversion/compression is too aggressive. Use an adequate source width and review quality settings and output visually at the rendered size.
Authenticated media fails through Next.js optimization The default optimization fetch does not forward headers to the remote source. Use an accessible origin or consider the documented unoptimized option for authenticated sources.
New sizes appear only for new uploads Previously uploaded media may not have those derivatives. Run an appropriate media regeneration workflow for existing attachments and verify the files exist.
WebP conversion does not affect derivatives WordPress sub-sizes normally retain the original format unless output handling is customized. Configure and verify the format pipeline, or move conversion to the delivery layer; test actual responses.

7. Performance, reliability, and cost

Image performance depends on the bytes the browser downloads, the number of requests, origin and optimization-layer behavior, and how well the selected candidate matches the layout. This dossier contains no benchmark for headless WordPress with WPGraphQL, so do not assume a fixed page-weight or load-time improvement from a particular format or query pattern.

Upload-time processing can make variants available before a page request, at the cost of storage and upload processing. Request-time optimization can adapt output to delivery needs, but adds a dependency on that service and its cache behavior. Keep originals where your editorial and recovery workflow needs them, and verify that generated variants can be recreated if lost.

For cost control, account for media storage, image processing, delivery or CDN charges, and any image service usage limits. Avoid generating derivatives that no template uses, and avoid repeatedly transforming the same source without a caching plan. Measure representative pages and inspect actual network responses rather than treating a format’s general compression claim as a site-specific result.

8. Or skip the browser setup

If you need screenshots of the rendered frontend for visual review or documentation, ScreenshotNeo provides a one-call website screenshot API. It is separate from the WordPress image optimization pipeline: it captures a page rather than resizing the site’s media assets. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Cookie banners, newsletter popups, and chat widgets are removed before the shot, and each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and billing status. An MCP server lets AI agents use screenshot, page-info, and PDF tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.

FAQ

Does WPGraphQL optimize or resize images?

No. It exposes media data through the GraphQL schema. Image size selection, responsive rendering, and any additional transformations belong to the WordPress media pipeline, frontend, or delivery layer.

Do I need Next.js to use WPGraphQL media?

No. Next.js is one implementation example. Other frontends should use their own image component or loader while applying the same principles: suitable asset sizes, reserved layout space, meaningful alt text, and responsive selection.

Should every image be converted to WebP?

Choose based on image quality, compatibility, transparency or animation needs, and the behavior of your actual delivery path. Test representative assets instead of assuming a fixed saving.

Can a screenshot confirm responsive image selection?

A screenshot shows the rendered appearance. Use browser network inspection to see which image URL and candidate were actually requested.