Repository: vercel/next.js
Pull request: feat(next/image): warn when images.localPatterns is undefined and src has query

Add a warning to Next.js 15.5 to prepare for Next.js 16 when `images.localPatterns` is undefined and the user attempted to optimize a `src` image with a query string, such as

```jsx
<Image src="/api/user?id=1" width="50" height="50" />
```

Note that we have default `localPatterns` so we need to account for that. See https://github.com/vercel/next.js/pull/70529

Changed files:
filename: errors/next-image-unconfigured-localpatterns.mdx
patch: @@ -10,6 +10,22 @@ One of your pages that leverages the `next/image` component, passed a `src` valu
 
 Add an entry to `images.localPatterns` array in `next.config.js` with the expected URL pattern. For example:
 
+```js filename="next.config.js"
+module.exports = {
+  images: {
+    localPatterns: [
+      {
+        pathname: '/assets/**',
+      },
+    ],
+  },
+}
+```
+
+Omitting the `search` will allow all query strings.
+
+If you want to prevent the query string from matching, you can use the empty string, for example:
+
 ```js filename="next.config.js"
 module.exports = {
   images: {

filename: packages/next/src/client/legacy/image.tsx
patch: @@ -866,6 +866,19 @@ export default function Image({
         )
       }
 
+      if (
+        src.startsWith('/') &&
+        src.includes('?') &&
+        (!config?.localPatterns?.length ||
+          (config.localPatterns.length === 1 &&
+            config.localPatterns[0].pathname === '/_next/static/media/**'))
+      ) {
+        warnOnce(
+          `Image with src "${src}" is using a query string which is not configured in images.localPatterns. This config will be required starting in Next.js 16.` +
+            `\nRead more: https://nextjs.org/docs/messages/next-image-unconfigured-localpatterns`
+        )
+      }
+
       if (!unoptimized && loader !== defaultImageLoader) {
         const urlStr = loader({
           config,

filename: packages/next/src/shared/lib/get-img-props.ts
patch: @@ -538,6 +538,18 @@ export function getImgProps(
           `\nRead more: https://nextjs.org/docs/messages/next-image-unconfigured-qualities`
       )
     }
+    if (
+      src.startsWith('/') &&
+      src.includes('?') &&
+      (!config?.localPatterns?.length ||
+        (config.localPatterns.length === 1 &&
+          config.localPatterns[0].pathname === '/_next/static/media/**'))
+    ) {
+      warnOnce(
+        `Image with src "${src}" is using a query string which is not configured in images.localPatterns. This config will be required starting in Next.js 16.` +
+          `\nRead more: https://nextjs.org/docs/messages/next-image-unconfigured-localpatterns`
+      )
+    }
     if (placeholder === 'blur' && !blurDataURL) {
       const VALID_BLUR_EXT = ['jpeg', 'png', 'webp', 'avif'] // should match next-image-loader
 

filename: test/unit/next-image-get-img-props.test.ts
patch: @@ -401,7 +401,9 @@ describe('getImageProps()', () => {
       width: 100,
       height: 200,
     })
-    expect(warningMessages).toStrictEqual([])
+    expect(warningMessages).toStrictEqual([
+      'Image with src "/test.svg?v=1" is using a query string which is not configured in images.localPatterns. This config will be required starting in Next.js 16.\nRead more: https://nextjs.org/docs/messages/next-image-unconfigured-localpatterns',
+    ])
     expect(Object.entries(props)).toStrictEqual([
       ['alt', 'a nice desc'],
       ['loading', 'lazy'],