Skip to content

Commit

Permalink
Warn in dev mode when stylesheets are added using next/head (vercel#3…
Browse files Browse the repository at this point in the history
…4004)

This commit adds a development mode warning in the console
if you try to include <link rel="stylesheet"> tags in
next/head, e.g.

```
<Head>
  <link ref="stylesheet" href="..." />
</Head>
```

The warning message explains that this pattern will not
work well with Suspense/streaming and recommends using a
custom Document component instead.

## Feature

- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [x] Errors have helpful link attached, see `contributing.md`


Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
  • Loading branch information
2 people authored and natew committed Feb 16, 2022
1 parent fc8a317 commit ced7805
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 5 deletions.
4 changes: 4 additions & 0 deletions errors/manifest.json
Expand Up @@ -488,6 +488,10 @@
"title": "script-tags-in-head-component",
"path": "/errors/no-script-tags-in-head-component.md"
},
{
"title": "stylesheets-in-head-component",
"path": "/errors/no-stylesheets-in-head-component.md"
},
{
"title": "max-custom-routes-reached",
"path": "/errors/max-custom-routes-reached.md"
Expand Down
42 changes: 42 additions & 0 deletions errors/no-stylesheets-in-head-component.md
@@ -0,0 +1,42 @@
# No Stylesheets In Head Component

### Why This Error Occurred

A `<link rel="stylesheet">` tag was added using the `next/head` component.

We don't recommend this pattern because it will potentially break when used with Suspense and/or streaming. In these contexts, `next/head` tags aren't:

- guaranteed to be included in the initial SSR response, so loading could be delayed until client-side rendering, regressing performance.

- loaded in any particular order. The order that the app's Suspense boundaries resolve will determine the loading order of your stylesheets.

### Possible Ways to Fix It

#### Document

Add the stylesheet in a custom `Document` component.

```jsx
// pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
return (
<Html>
<Head>
<link rel="stylesheet" href="..." />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
```

Note that the functional syntax for `Document` above is preferred over the `class` syntax, so that it will be compatible with React Server Components down the line.

### Useful Links

- [Custom Document](https://nextjs.org/docs/advanced-features/custom-document)
18 changes: 13 additions & 5 deletions packages/next/shared/lib/head.tsx
Expand Up @@ -161,11 +161,19 @@ function reduceComponents(
return React.cloneElement(c, newProps)
}
}
// TODO(kara): warn for stylesheets as well as scripts
if (process.env.NODE_ENV === 'development' && c.type === 'script') {
console.warn(
`Do not add <script> tags using next/head. Use next/script instead. \nSee more info here: https://nextjs.org/docs/messages/no-script-tags-in-head-component`
)
if (process.env.NODE_ENV === 'development') {
if (c.type === 'script') {
const srcMessage = c.props['src']
? `<script> tag with src="${c.props['src']}"`
: `inline <script>`
console.warn(
`Do not add <script> tags using next/head (see ${srcMessage}). Use next/script instead. \nSee more info here: https://nextjs.org/docs/messages/no-script-tags-in-head-component`
)
} else if (c.type === 'link' && c.props['rel'] === 'stylesheet') {
console.warn(
`Do not add stylesheets using next/head (see <link rel="stylesheet"> tag with href="${c.props['href']}"). Use Document instead. \nSee more info here: https://nextjs.org/docs/messages/no-stylesheets-in-head-component`
)
}
}
return React.cloneElement(c, { key })
})
Expand Down
23 changes: 23 additions & 0 deletions test/integration/client-navigation/test/index.test.js
Expand Up @@ -1414,6 +1414,29 @@ describe('Client Navigation', () => {
}
})

it('should warn when stylesheets are in head', async () => {
let browser
try {
browser = await webdriver(context.appPort, '/head')

await browser.waitForElementByCss('h1')
await waitFor(2000)
const browserLogs = await browser.log('browser')
let found = false
browserLogs.forEach((log) => {
console.log('log.message', log.message)
if (log.message.includes('Do not add stylesheets using next/head')) {
found = true
}
})
expect(found).toEqual(true)
} finally {
if (browser) {
await browser.close()
}
}
})

it('should update head during client routing', async () => {
let browser
try {
Expand Down

0 comments on commit ced7805

Please sign in to comment.