Repository: vercel/next.js
Pull request: feat(next/legacy/image)!: deprecate and warn on `next/legacy/image` usage

`next/legacy/image` was introduced in Next.js 13 as way to allow developers to upgrade from 12 to 13 with minimal effort.

Now that its been 3 years, its time to deprecate `next/legacy/image` in Next.js 16 so we can remove it in a future version.

This will print a warning the first time the component is used.

Changed files:
filename: docs/02-pages/04-api-reference/01-components/image-legacy.mdx
patch: @@ -6,7 +6,7 @@ version: legacy
 
 Starting with Next.js 13, the `next/image` component was rewritten to improve both the performance and developer experience. In order to provide a backwards compatible upgrade solution, the old `next/image` was renamed to `next/legacy/image`.
 
-View the **new** [`next/image` API Reference](/docs/pages/api-reference/components/image)
+> **Warning**: `next/legacy/image` is deprecated and will be removed in a future version of Next.js. Please use [`next/image`](/docs/app/api-reference/components/image) instead.
 
 ## Comparison
 
@@ -642,6 +642,7 @@ Auto-detection for animated files is best-effort and supports GIF, APNG, and Web
 
 ## Version History
 
-| Version   | Changes                                     |
-| --------- | ------------------------------------------- |
-| `v13.0.0` | `next/image` renamed to `next/legacy/image` |
+| Version   | Changes                                                                                                             |
+| --------- | ------------------------------------------------------------------------------------------------------------------- |
+| `v16.0.0` | `next/legacy/image` deprecated and will be removed in a future version of Next.js. Please use `next/image` instead. |
+| `v13.0.0` | `next/image` renamed to `next/legacy/image`                                                                         |

filename: packages/next/src/client/legacy/image.tsx
patch: @@ -616,6 +616,9 @@ const ImageElement = ({
   )
 }
 
+/**
+ * @deprecated The `next/legacy/image` component is deprecated and will be removed in a future version of Next.js. Please use `next/image` instead.
+ */
 export default function Image({
   src,
   sizes,
@@ -697,6 +700,10 @@ export default function Image({
   }
   src = typeof src === 'string' ? src : staticSrc
 
+  warnOnce(
+    `Image with src "${src}" is using next/legacy/image which is deprecated and will be removed in a future version of Next.js.`
+  )
+
   let isLazy =
     !priority && (loading === 'lazy' || typeof loading === 'undefined')
   if (src.startsWith('data:') || src.startsWith('blob:')) {

filename: test/integration/next-image-legacy/default/test/index.test.ts
patch: @@ -1057,13 +1057,17 @@ function runTests(mode) {
       const warnings = (await browser.log())
         .map((log) => log.message)
         .filter((log) => log.startsWith('Image with src'))
+
       expect(warnings[0]).toMatch(
-        'Image with src "/test.png" has "sizes" property but it will be ignored.'
+        'Image with src "/test.png" is using next/legacy/image which is deprecated and will be removed in a future version of Next.js.'
       )
       expect(warnings[1]).toMatch(
+        'Image with src "/test.png" has "sizes" property but it will be ignored.'
+      )
+      expect(warnings[2]).toMatch(
         'Image with src "/test.png" was detected as the Largest Contentful Paint (LCP).'
       )
-      expect(warnings.length).toBe(2)
+      expect(warnings.length).toBe(3)
     })
   } else {
     //server-only tests

filename: test/integration/next-image-new/app-dir/test/index.test.ts
patch: @@ -398,6 +398,9 @@ function runTests(mode: 'dev' | 'server') {
       expect(warnings).not.toMatch(
         /was detected as the Largest Contentful Paint/gm
       )
+      expect(warnings).not.toMatch(
+        'using next/legacy/image which is deprecated and will be removed in a future version'
+      )
       expect(warnings).not.toMatch(/React does not recognize the (.+) prop/gm)
     } finally {
       if (browser) {