diff --git a/docs/basic-features/eslint.md b/docs/basic-features/eslint.md
index a8c99aea6e3ba..b85d16f44cacb 100644
--- a/docs/basic-features/eslint.md
+++ b/docs/basic-features/eslint.md
@@ -98,6 +98,7 @@ Next.js provides an ESLint plugin, [`eslint-plugin-next`](https://www.npmjs.com/
| ✔️ | [next/inline-script-id](/docs/messages/inline-script-id.md) | Enforce id attribute on next/script components with inline content |
| ✔️ | next/no-typos | Ensure no typos were made declaring [Next.js's data fetching function](/docs/basic-features/data-fetching/overview.md) |
| ✔️ | [next/next-script-for-ga](/docs/messages/next-script-for-ga.md) | Use the Script component to defer loading of the script until necessary. |
+| ✔️ | [next/no-styled-jsx-in-document](/docs/messages/no-styled-jsx-in-document.md) | styled-jsx should not be used in \_document |
- ✔: Enabled in the recommended configuration
diff --git a/errors/manifest.json b/errors/manifest.json
index fe726f264ff89..79adcf8104af1 100644
--- a/errors/manifest.json
+++ b/errors/manifest.json
@@ -398,6 +398,10 @@
"title": "no-server-import-in-page",
"path": "/errors/no-server-import-in-page.md"
},
+ {
+ "title": "no-styled-jsx-in-document",
+ "path": "/errors/no-styled-jsx-in-document.md"
+ },
{
"title": "no-sync-scripts",
"path": "/errors/no-sync-scripts.md"
diff --git a/errors/no-styled-jsx-in-document.md b/errors/no-styled-jsx-in-document.md
new file mode 100644
index 0000000000000..4c9dbf6133386
--- /dev/null
+++ b/errors/no-styled-jsx-in-document.md
@@ -0,0 +1,41 @@
+# No styled-jsx in Document
+
+### Why This Error Occurred
+
+Custom CSS like `styled-jsx` is not allowed in a [Custom Document](https://nextjs.org/docs/advanced-features/custom-document).
+
+### Possible Ways to Fix It
+
+If you need shared CSS for all of your pages, take a look at the [Custom `App`](https://nextjs.org/docs/advanced-features/custom-app) file or define a custom layout.
+
+For example, consider the following stylesheet named `styles.css`:
+
+```css
+body {
+ font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', 'Helvetica',
+ 'Arial', sans-serif;
+ padding: 20px 20px 60px;
+ max-width: 680px;
+ margin: 0 auto;
+}
+```
+
+Create a `pages/_app.{js,tsx}` file if not already present. Then, import the `styles.css` file.
+
+```jsx
+import '../styles.css'
+
+// This default export is required in a new `pages/_app.js` file.
+export default function MyApp({ Component, pageProps }) {
+ return
+}
+```
+
+These styles (`styles.css`) will apply to all pages and components in your application.
+
+### Useful links
+
+- [Custom Document Caveats](https://nextjs.org/docs/advanced-features/custom-document#caveats)
+- [Layouts](https://nextjs.org/docs/basic-features/layouts)
+- [Built in CSS Support](https://nextjs.org/docs/basic-features/built-in-css-support)
+- [Custom `App`](https://nextjs.org/docs/advanced-features/custom-app)
diff --git a/packages/eslint-plugin-next/lib/index.js b/packages/eslint-plugin-next/lib/index.js
index 84b77fddeb32c..9c1b1559b8fc4 100644
--- a/packages/eslint-plugin-next/lib/index.js
+++ b/packages/eslint-plugin-next/lib/index.js
@@ -14,6 +14,7 @@ module.exports = {
'no-head-import-in-document': require('./rules/no-head-import-in-document'),
'no-script-component-in-head': require('./rules/no-script-component-in-head'),
'no-server-import-in-page': require('./rules/no-server-import-in-page'),
+ 'no-styled-jsx-in-document': require('./rules/no-styled-jsx-in-document'),
'no-typos': require('./rules/no-typos'),
'no-duplicate-head': require('./rules/no-duplicate-head'),
'inline-script-id': require('./rules/inline-script-id'),
@@ -39,6 +40,7 @@ module.exports = {
'@next/next/no-document-import-in-page': 2,
'@next/next/no-head-import-in-document': 2,
'@next/next/no-script-component-in-head': 2,
+ '@next/next/no-styled-jsx-in-document': 2,
'@next/next/no-server-import-in-page': 2,
'@next/next/no-typos': 1,
'@next/next/no-duplicate-head': 2,
diff --git a/packages/eslint-plugin-next/lib/rules/no-styled-jsx-in-document.js b/packages/eslint-plugin-next/lib/rules/no-styled-jsx-in-document.js
new file mode 100644
index 0000000000000..5851c80cb1e83
--- /dev/null
+++ b/packages/eslint-plugin-next/lib/rules/no-styled-jsx-in-document.js
@@ -0,0 +1,46 @@
+const path = require('path')
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'Disallow using custom styled-jsx inside pages/_document.js',
+ recommended: true,
+ url: 'https://nextjs.org/docs/messages/no-styled-jsx-in-document',
+ },
+ fixable: 'code',
+ },
+ create: function (context) {
+ return {
+ JSXOpeningElement(node) {
+ const document = context.getFilename().split('pages')[1]
+ if (!document) {
+ return
+ }
+ const { name, dir } = path.parse(document)
+
+ if (
+ !(
+ name.startsWith('_document') ||
+ (dir === '/_document' && name === 'index')
+ )
+ ) {
+ return
+ }
+
+ if (
+ node.name.name === 'style' &&
+ node.attributes.find(
+ (attr) => attr.type === 'JSXAttribute' && attr.name.name === 'jsx'
+ )
+ ) {
+ context.report({
+ node,
+ message: `styled-jsx can not be used inside pages/_document.js. See https://nextjs.org/docs/messages/no-styled-jsx-in-document.`,
+ })
+ }
+ },
+ }
+ },
+}
+
+module.exports.schema = []
diff --git a/test/unit/eslint-plugin-next/no-styled-jsx-in-document.test.ts b/test/unit/eslint-plugin-next/no-styled-jsx-in-document.test.ts
new file mode 100644
index 0000000000000..b6cce7043dbe1
--- /dev/null
+++ b/test/unit/eslint-plugin-next/no-styled-jsx-in-document.test.ts
@@ -0,0 +1,125 @@
+import rule from '@next/eslint-plugin-next/lib/rules/no-styled-jsx-in-document'
+import { RuleTester } from 'eslint'
+;(RuleTester as any).setDefaultConfig({
+ parserOptions: {
+ ecmaVersion: 2018,
+ sourceType: 'module',
+ ecmaFeatures: {
+ modules: true,
+ jsx: true,
+ },
+ },
+})
+const ruleTester = new RuleTester()
+
+ruleTester.run('no-styled-jsx-in-document', rule, {
+ valid: [
+ {
+ filename: 'pages/_document.js',
+ code: `import Document, { Html, Head, Main, NextScript } from 'next/document'
+
+ export class MyDocument extends Document {
+ static async getInitialProps(ctx) {
+ const initialProps = await Document.getInitialProps(ctx)
+ return { ...initialProps }
+ }
+
+ render() {
+ return (
+
+
+
+
+
+
+ )
+ }
+ }`,
+ },
+ {
+ filename: 'pages/_document.js',
+ code: `import Document, { Html, Head, Main, NextScript } from 'next/document'
+
+ export class MyDocument extends Document {
+ static async getInitialProps(ctx) {
+ const initialProps = await Document.getInitialProps(ctx)
+ return { ...initialProps }
+ }
+
+ render() {
+ return (
+
+
+
+
+
+
+ )
+ }
+ }`,
+ },
+ {
+ filename: 'pages/index.js',
+ code: `
+ export default function Page() {
+ return (
+ <>
+ Hello world
+
+ >
+ )
+ }
+ `,
+ },
+ ],
+
+ invalid: [
+ {
+ filename: 'pages/_document.js',
+ code: `
+ import Document, { Html, Head, Main, NextScript } from 'next/document'
+
+ export class MyDocument extends Document {
+ static async getInitialProps(ctx) {
+ const initialProps = await Document.getInitialProps(ctx)
+ return { ...initialProps }
+ }
+
+ render() {
+ return (
+
+
+
+
+
+
+ )
+ }
+ }`,
+ errors: [
+ {
+ message:
+ 'styled-jsx can not be used inside pages/_document.js. See https://nextjs.org/docs/messages/no-styled-jsx-in-document.',
+ },
+ ],
+ },
+ ],
+})