Skip to content

Commit d72f5bd

Browse files
[ESLint] Adds lint rule to flag usage of <head> (#32897)
## Bug - [X] Related issues linked using `fixes #number` - [X] Tests added - [X] Errors have helpful link attached, see `contributing.md` Fixes #30142
1 parent 636d3ae commit d72f5bd

File tree

6 files changed

+162
-0
lines changed

6 files changed

+162
-0
lines changed

docs/basic-features/eslint.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ Next.js provides an ESLint plugin, [`eslint-plugin-next`](https://www.npmjs.com/
9090
| ✔️ | [next/no-head-import-in-document](https://nextjs.org/docs/messages/no-head-import-in-document) | Disallow importing next/head in pages/document.js |
9191
| ✔️ | [next/no-html-link-for-pages](https://nextjs.org/docs/messages/no-html-link-for-pages) | Prohibit HTML anchor links to pages without a Link component |
9292
| ✔️ | [next/no-img-element](https://nextjs.org/docs/messages/no-img-element) | Prohibit usage of HTML &lt;img&gt; element |
93+
| ✔️ | [next/no-head-element](https://nextjs.org/docs/messages/no-head-element) | Prohibit usage of HTML &lt;head&gt; element |
9394
| ✔️ | [next/no-page-custom-font](https://nextjs.org/docs/messages/no-page-custom-font) | Prevent page-only custom fonts |
9495
| ✔️ | [next/no-sync-scripts](https://nextjs.org/docs/messages/no-sync-scripts) | Forbid synchronous scripts |
9596
| ✔️ | [next/no-title-in-document-head](https://nextjs.org/docs/messages/no-title-in-document-head) | Disallow using &lt;title&gt; with Head from next/document |

errors/manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@
447447
"path": "/errors/link-multiple-children.md"
448448
},
449449
{ "title": "no-img-element", "path": "/errors/no-img-element.md" },
450+
{ "title": "no-head-element", "path": "/errors/no-head-element.md" },
450451
{
451452
"title": "non-dynamic-getstaticpaths-usage",
452453
"path": "/errors/non-dynamic-getstaticpaths-usage.md"

errors/no-head-element.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# No Head Element
2+
3+
### Why This Error Occurred
4+
5+
An HTML `<head>` element was used to include page-level metadata, but this can cause unexpected behavior in a Next.js application. Use Next.js' built-in `<Head />` component instead.
6+
7+
### Possible Ways to Fix It
8+
9+
Import and use the `<Head />` component:
10+
11+
```jsx
12+
import Head from 'next/head'
13+
14+
function Index() {
15+
return (
16+
<>
17+
<Head>
18+
<title>My page title</title>
19+
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
20+
</Head>
21+
</>
22+
)
23+
}
24+
25+
export default Index
26+
```
27+
28+
### Useful Links
29+
30+
- [next/head](https://nextjs.org/docs/api-reference/next/head)

packages/eslint-plugin-next/lib/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module.exports = {
44
'no-sync-scripts': require('./rules/no-sync-scripts'),
55
'no-html-link-for-pages': require('./rules/no-html-link-for-pages'),
66
'no-img-element': require('./rules/no-img-element'),
7+
'no-head-element': require('./rules/no-head-element'),
78
'no-unwanted-polyfillio': require('./rules/no-unwanted-polyfillio'),
89
'no-page-custom-font': require('./rules/no-page-custom-font'),
910
'no-title-in-document-head': require('./rules/no-title-in-document-head'),
@@ -28,6 +29,7 @@ module.exports = {
2829
'@next/next/no-sync-scripts': 1,
2930
'@next/next/no-html-link-for-pages': 1,
3031
'@next/next/no-img-element': 1,
32+
'@next/next/no-head-element': 1,
3133
'@next/next/no-unwanted-polyfillio': 1,
3234
'@next/next/no-page-custom-font': 1,
3335
'@next/next/no-title-in-document-head': 1,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module.exports = {
2+
meta: {
3+
docs: {
4+
description: 'Prohibit usage of HTML <head> element',
5+
category: 'HTML',
6+
recommended: true,
7+
url: 'https://nextjs.org/docs/messages/no-head-element',
8+
},
9+
fixable: 'code',
10+
},
11+
12+
create: function (context) {
13+
return {
14+
JSXOpeningElement(node) {
15+
if (node.name.name !== 'head') {
16+
return
17+
}
18+
19+
context.report({
20+
node,
21+
message: `Do not use <head>. Use Head from 'next/head' instead. See: https://nextjs.org/docs/messages/no-head-element`,
22+
})
23+
},
24+
}
25+
},
26+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import rule from '@next/eslint-plugin-next/lib/rules/no-head-element'
2+
import { RuleTester } from 'eslint'
3+
;(RuleTester as any).setDefaultConfig({
4+
parserOptions: {
5+
ecmaVersion: 2018,
6+
sourceType: 'module',
7+
ecmaFeatures: {
8+
modules: true,
9+
jsx: true,
10+
},
11+
},
12+
})
13+
const ruleTester = new RuleTester()
14+
15+
ruleTester.run('no-head-element', rule, {
16+
valid: [
17+
{
18+
code: `import Head from 'next/head';
19+
20+
export class MyComponent {
21+
render() {
22+
return (
23+
<div>
24+
<Head>
25+
<title>My page title</title>
26+
</Head>
27+
</div>
28+
);
29+
}
30+
}
31+
`,
32+
filename: 'pages/index.js',
33+
},
34+
{
35+
code: `import Head from 'next/head';
36+
37+
export class MyComponent {
38+
render() {
39+
return (
40+
<div>
41+
<Head>
42+
<title>My page title</title>
43+
</Head>
44+
</div>
45+
);
46+
}
47+
}
48+
`,
49+
filename: 'pages/index.tsx',
50+
},
51+
],
52+
invalid: [
53+
{
54+
code: `
55+
export class MyComponent {
56+
render() {
57+
return (
58+
<div>
59+
<head>
60+
<title>My page title</title>
61+
</head>
62+
</div>
63+
);
64+
}
65+
}`,
66+
filename: 'pages/index.js',
67+
errors: [
68+
{
69+
message:
70+
"Do not use <head>. Use Head from 'next/head' instead. See: https://nextjs.org/docs/messages/no-head-element",
71+
type: 'JSXOpeningElement',
72+
},
73+
],
74+
},
75+
{
76+
code: `import Head from 'next/head';
77+
78+
export class MyComponent {
79+
render() {
80+
return (
81+
<div>
82+
<head>
83+
<title>My page title</title>
84+
</head>
85+
<Head>
86+
<title>My page title</title>
87+
</Head>
88+
</div>
89+
);
90+
}
91+
}`,
92+
filename: 'pages/index.ts',
93+
errors: [
94+
{
95+
message:
96+
"Do not use <head>. Use Head from 'next/head' instead. See: https://nextjs.org/docs/messages/no-head-element",
97+
type: 'JSXOpeningElement',
98+
},
99+
],
100+
},
101+
],
102+
})

0 commit comments

Comments
 (0)