The media library move

Migrate WordPress Images to Next.js Without Losing Alt Text, Captions or Image Search Traffic

Upload one photo to WordPress and you get half a dozen files, a database row and an alt text field most people never filled in. Moving that library to Next.js is mostly plumbing. The parts that cost rankings are the alt text, the captions and the image URLs Google already knows.

October 8, 2026 · 9 min read

The short answer

Export the media library through the WordPress REST API to get each original file with its alt text, caption and parent post. Move only the originals, since next/image builds its own resized copies and modern formats. Keep or permanently redirect any image URL that earns Google Images traffic, rewrite image references inside post content, and set sizes and loading on hero images.

On this page
  1. One upload, a folder full of copies
  2. Alt text lives in two places, and they disagree
  3. Image URLs that already rank in Google Images
  4. What next/image does with the files you bring
  5. CDNs, offload plugins and image URLs on someone else's domain
  6. The image migration, step by step
  7. After the move: one library, described and sized

One upload, a folder full of copies

WordPress stores media under /wp-content/uploads/, usually in year and month folders such as /2024/03/ when the Media setting to organize uploads that way is on. Each upload is saved as the original plus a resized copy for every registered image size. Core registers thumbnail, medium, medium_large and large, plus the 1536 and 2048 pixel sizes added in WordPress 5.3. Themes and plugins add more: WooCommerce registers its own product image sizes, and many themes register three or four of their own.

Since WordPress 5.3, any upload wider or taller than 2,560 pixels is also scaled down and saved with -scaled in the filename, while the untouched original stays on disk beside it. Optimization plugins may then add WebP copies of every one of those files. The result is a folder where a single product photo can exist as a dozen files, named things like hero-1024x683.jpg and hero-scaled.jpg.

Almost none of that needs to move. A Next.js build makes its own resized copies on request, so the migration carries the originals and leaves the rest behind, with the exception of the copies that search engines have indexed, which we come to below. Theme matters here too: image-heavy stores built on WoodMart or Flatsome tend to have the largest libraries and the most theme-registered sizes.

Alt text lives in two places, and they disagree

Every upload in WordPress is also a post, of the attachment type. Its title is the attachment title, its caption is stored as the excerpt, its description as the content, and its alt text in a separate meta field called _wp_attachment_image_alt. The REST API exposes all of it at /wp-json/wp/v2/media, along with the parent post and the list of generated sizes.

Here is the catch. When an editor inserts an image into a post, WordPress copies the alt text into the post's HTML at that moment. If someone later improves the alt text in the Media Library, old posts keep the old version, or none at all. Featured images behave differently, because themes usually read the alt field live. So one photo can carry three different descriptions depending on where you look.

A careful migration reads both. We take the alt text written into each post's HTML, compare it with the library field, and keep the better one, flagging images that have neither. Captions get the same treatment, since the block editor stores them as figcaption text inside the post while the library keeps its own copy. On Athena, Picasso then keeps the new library honest: images described, sized and never missing.

Image URLs that already rank in Google Images

Open Search Console, choose Performance, and switch the search type to Image. If rows appear, some of your image files are bringing in visitors, and their exact URLs are part of what ranks. Google's site move guidance tells you to include images and other embedded files in your plans. A recipe site, a product catalog or a portfolio can get a real share of its traffic this way without the owner ever noticing.

There are two honest options for those files. The first is to keep them at their old addresses: the new site serves /wp-content/uploads/2024/03/photo.jpg from storage exactly as before, even though no new upload will ever use that path. The second is a permanent redirect from each old file URL to its new home. Keeping is simpler and loses nothing. Redirecting is tidier and works well when the list is short. Either way, the decision covers the resized copies that were indexed, such as photo-1024x683.jpg, not just the originals.

Google's image guidance adds two points that shape the build. Google finds images in the src of an img element and does not index CSS background images, so any picture that should rank must be a real image element, not a decorative background. And image sitemaps can list image URLs on other domains, which helps when files move to a CDN host. The SEO-safe migration checklist covers the page side of the same move.

Then there is the filename question. Google's guidance prefers descriptive names over camera defaults, and a library full of IMG_0423.jpg is tempting to clean up while everything is in motion. Rename freely where a file has no image search traffic, because nothing outside your own pages points at it. Leave the ranking files alone, or rename them with a redirect from the old name. A tidy filename is not worth the impressions a known one already collects.

What next/image does with the files you bring

Next.js ships an Image component that replaces the hand-rolled srcset WordPress builds from its stored copies. According to the Next.js documentation, src and alt are required, and the component needs either width and height or the fill prop. It then generates the srcset and src for you, pointing at the image optimization endpoint, /_next/image by default, with the width and quality in the query string.

  • Formats: Next.js reads the browser's Accept header and serves WebP by default. AVIF can be switched on in the images config, with WebP as the fallback.
  • Sizes: with a sizes prop, the component builds a full width-based srcset from the configured device and image sizes, so phones download phone-sized files.
  • Lazy loading: images load lazily by default. For the hero image that is usually your Largest Contentful Paint, set loading to eager or fetchPriority to high. In Next.js 16 the old priority prop is deprecated in favor of preload.
  • Remote files: images served from another host, such as a storage bucket, must be allowed with remotePatterns in next.config.
  • Outside services: a loaderFile setting hands optimization to an image CDN instead of Next.js.

Because width and height are declared, the browser reserves space before the file arrives, which removes one of the most common sources of layout shift on old WordPress themes. The mechanics are in WordPress vs Next.js on LCP and on CLS.

The third option

Athena CMS, with AI agents on staff.

Not a theme and not a blank Next.js repo. The content system we put behind every custom site: plain fields, a real preview, every version kept, and AI agents that handle the chores a plugin only nags you about.

and 12 more agents. Two come with Athena; the rest are monthly add-ons.

The Athena CMS editor: plain fields with AI rewrite, a Publish box and revisions

CDNs, offload plugins and image URLs on someone else's domain

Not every WordPress image lives in /wp-content/uploads/. Jetpack's image CDN serves files from i0.wp.com and its siblings. Offload plugins move the library to a storage bucket and rewrite URLs to a CDN domain. Some page builders store absolute image URLs, domain and all, inside their layout data. A crawl of the live site shows which hosts your pages actually load images from, and every one of them goes on the inventory.

On the Next.js side there are three common setups. Small sites can keep originals in the project and let the framework optimize them. Larger libraries usually go into object storage, allowed through remotePatterns, so the build does not carry gigabytes of photos. Sites with heavy traffic or very large catalogs can hand the work to a dedicated image service through loaderFile. All three work with Athena, which stores the image record, alt text and caption as fields alongside the file.

Content written on a Divi or Elementor site needs extra care, because image URLs sit inside shortcode attributes or builder JSON rather than plain img tags. The extraction script has to read the builder's format to find them, or the new pages end up pointing at the old server.

The image migration, step by step

  1. 1

    Pull the media records

    Read every attachment from the REST API media endpoint: original file URL, alt text, caption, title, parent post and generated sizes.

  2. 2

    Download originals only

    Fetch each original file, or the -scaled version where the original is oversized and was never used. Skip theme and plugin copies.

  3. 3

    Find the images Google knows

    Export Image search results from Search Console and match each ranking URL, including resized copies, to its original.

  4. 4

    Rewrite references in content

    Replace old image URLs inside posts, pages and builder data with new records, carrying the in-content alt text and caption.

  5. 5

    Keep or redirect old file URLs

    Serve ranking files at their old paths or add permanent redirects to the new files, and add an image sitemap.

  6. 6

    Set sizes and loading

    Give every image template a sizes value, mark hero images eager or high priority, and check the result in PageSpeed Insights.

Orphaned files are the one place we encourage deletion. Libraries collect uploads that no page uses anymore. Unless they rank on their own, they stay behind with the old server.

After the move: one library, described and sized

The point of all this is a media library you never have to think about again. In Athena CMS an image is a record with plain fields for alt text and caption, a real preview of how it sits on the page, and every version kept. Editors upload one file and the front end produces the sizes and formats. There is no regenerate-thumbnails plugin to run after a theme change and no optimization plugin with a monthly quota.

Picasso, the agent that handles images, flags uploads without descriptions, catches oversized files and checks that nothing on the site points at an image that no longer exists. Argus watches for slow pages, which often turn out to be one enormous photo. If you would rather hand the whole move to someone, our WordPress to Next.js development service covers images along with content, URLs and redirects. Start at the migration hub for the rest of the series.

Questions people ask before they call

No. Move the originals. next/image generates resized versions and modern formats on request, so the theme and plugin copies in the uploads folder are redundant. The exception is any resized copy that appears in Google Images results. Those keep their old URL or get a permanent redirect to the new file.

It will if the migration reads it from the right places. WordPress stores alt text on the attachment and also copies it into each post's HTML when the image is inserted, and the two often differ. We compare both, keep the better one and list the images that have none, so the move improves your alt text rather than losing it.

The optimized files are served through the image optimization endpoint, /_next/image by default, with width and quality in the query string. The original files keep whatever address you give them. For images that already rank, we keep the original files reachable at their old paths or redirect them, and list them in an image sitemap.

Yes. Images hosted on another domain are allowed with remotePatterns in next.config, and an outside image service can take over optimization entirely through the loaderFile setting. Which setup fits depends on library size and traffic. We cover the choice in every Next.js rebuild we quote.

They need extraction from the builder's own format, because the URLs sit in shortcode attributes or layout data rather than ordinary image tags. Our scripts read those formats directly. The Elementor migration guide and the Divi guide explain how builder content is converted.

Keep reading

Free · No obligation

Find out what your media library is hiding

Send us your site. We will check how many images it holds, how many lack alt text, which ones bring in search traffic and what the move involves.

Which of these sounds like you? (pick any)

Rather talk now? Call (210) 346-0848.

CallGet a quote