diff --git a/packages/eslint-plugin-next/src/rules/no-img-element.ts b/packages/eslint-plugin-next/src/rules/no-img-element.ts
index 01511b96fec9a..2c3f5f2c92e13 100644
--- a/packages/eslint-plugin-next/src/rules/no-img-element.ts
+++ b/packages/eslint-plugin-next/src/rules/no-img-element.ts
@@ -1,3 +1,4 @@
+import path = require('path')
import { defineRule } from '../utils/define-rule'
const url = 'https://nextjs.org/docs/messages/no-img-element'
@@ -15,6 +16,14 @@ export = defineRule({
schema: [],
},
create(context) {
+ // Get relative path of the file
+ const relativePath = context.filename
+ .replace(path.sep, '/')
+ .replace(context.cwd, '')
+ .replace(/^\//, '')
+
+ const isAppDir = /^(src\/)?app\//.test(relativePath)
+
return {
JSXOpeningElement(node) {
if (node.name.name !== 'img') {
@@ -29,6 +38,14 @@ export = defineRule({
return
}
+ // If is metadata route files, ignore
+ // e.g. opengraph-image.js, twitter-image.js, icon.js
+ if (
+ isAppDir &&
+ /\/opengraph-image|twitter-image|icon\.\w+$/.test(relativePath)
+ )
+ return
+
context.report({
node,
message: `Using \`\` could result in slower LCP and higher bandwidth. Consider using \`\` from \`next/image\` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: ${url}`,
diff --git a/test/unit/eslint-plugin-next/no-img-element.test.ts b/test/unit/eslint-plugin-next/no-img-element.test.ts
index 9f636883b7d59..8477bace54c2b 100644
--- a/test/unit/eslint-plugin-next/no-img-element.test.ts
+++ b/test/unit/eslint-plugin-next/no-img-element.test.ts
@@ -55,6 +55,46 @@ const tests = {
);
}
}`,
+ {
+ code: `\
+import { ImageResponse } from "next/og";
+
+export default function icon() {
+ return new ImageResponse(
+ (
+
+ )
+ );
+}
+`,
+ filename: `src/app/icon.js`,
+ },
+ {
+ code: `\
+import { ImageResponse } from "next/og";
+
+export default function Image() {
+ return new ImageResponse(
+ (
+
+ )
+ );
+}
+`,
+ filename: `app/opengraph-image.tsx`,
+ },
],
invalid: [
{
@@ -91,6 +131,27 @@ const tests = {
}`,
errors: [{ message, type: 'JSXOpeningElement' }],
},
+ {
+ code: `\
+import { ImageResponse } from "next/og";
+
+export default function Image() {
+return new ImageResponse(
+ (
+
+ )
+);
+}
+`,
+ filename: `some/non-metadata-route-image.tsx`,
+ errors: [{ message, type: 'JSXOpeningElement' }],
+ },
],
}