diff --git a/errors/context-in-server-component.md b/errors/context-in-server-component.md
new file mode 100644
index 000000000000000..1dcbfd8a2f7ce39
--- /dev/null
+++ b/errors/context-in-server-component.md
@@ -0,0 +1,30 @@
+# createContext in a Server Component
+
+#### Why This Error Occurred
+
+You are using `createContext` in a Server Component but it only works in Client Components.
+
+#### Possible Ways to Fix It
+
+Mark the component using `createContext` as a Client Component by adding `'use client'` at the top of the file.
+
+##### Before
+
+```jsx
+import { createContext } from 'react'
+
+const Context = createContext()
+```
+
+##### After
+
+```jsx
+'use client'
+import { createContext } from 'react'
+
+const Context = createContext()
+```
+
+### Useful Links
+
+[Server and Client Components](https://beta.nextjs.org/docs/rendering/server-and-client-components#context)
diff --git a/errors/manifest.json b/errors/manifest.json
index f2ed0ae1656e749..7273d829894c5ec 100644
--- a/errors/manifest.json
+++ b/errors/manifest.json
@@ -773,6 +773,10 @@
{
"title": "fast-refresh-reload",
"path": "/errors/fast-refresh-reload.md"
+ },
+ {
+ "title": "context-in-server-component",
+ "path": "/errors/context-in-server-component.md"
}
]
}
diff --git a/packages/next/lib/format-server-error.ts b/packages/next/lib/format-server-error.ts
new file mode 100644
index 000000000000000..477a837741d308d
--- /dev/null
+++ b/packages/next/lib/format-server-error.ts
@@ -0,0 +1,12 @@
+export function formatServerError(error: Error): void {
+ if (error.message.includes('createContext is not a function')) {
+ const message =
+ 'createContext only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/context-in-server-component'
+ error.message = message
+ if (error.stack) {
+ const lines = error.stack.split('\n')
+ lines[0] = message
+ error.stack = lines.join('\n')
+ }
+ }
+}
diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts
index 3b793ba777e35bc..336686fbe51f339 100644
--- a/packages/next/server/dev/next-dev-server.ts
+++ b/packages/next/server/dev/next-dev-server.ts
@@ -76,6 +76,7 @@ import {
} from '../../build/utils'
import { getDefineEnv } from '../../build/webpack-config'
import loadJsConfig from '../../build/load-jsconfig'
+import { formatServerError } from '../../lib/format-server-error'
// Load ReactDevOverlay only when needed
let ReactDevOverlayImpl: FunctionComponent
@@ -1027,6 +1028,7 @@ export default class DevServer extends Server {
return await super.run(req, res, parsedUrl)
} catch (error) {
const err = getProperError(error)
+ formatServerError(err)
this.logErrorWithOriginalStack(err).catch(() => {})
if (!res.sent) {
res.statusCode = 500
diff --git a/test/development/acceptance-app/__snapshots__/server-components.test.ts.snap b/test/development/acceptance-app/__snapshots__/server-components.test.ts.snap
new file mode 100644
index 000000000000000..5c76475e0fc1da1
--- /dev/null
+++ b/test/development/acceptance-app/__snapshots__/server-components.test.ts.snap
@@ -0,0 +1,42 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Error Overlay for server components createContext called in Server Component should show error when React.createContext is called 1`] = `
+" 1 of 1 unhandled error
+Server Error
+
+TypeError: createContext only works in Client Components. Add the \\"use client\\" directive at the top of the file to use it. Read more: https://beta.nextjs.org/docs/rendering/server-and-client-components#context
+
+This error happened while generating the page. Any console logs will be displayed in the terminal window.
+
+app/page.js (3:24) @ React
+
+ 1 |
+ 2 | import React from 'react'
+> 3 | const Context = React.createContext()
+ | ^
+ 4 | export default function Page() {
+ 5 | return (
+ 6 | <>"
+`;
+
+exports[`Error Overlay for server components createContext called in Server Component should show error when React.createContext is called in external package 1`] = `
+" 1 of 1 unhandled error
+Server Error
+
+TypeError: createContext only works in Client Components. Add the \\"use client\\" directive at the top of the file to use it. Read more: https://beta.nextjs.org/docs/rendering/server-and-client-components#context
+
+This error happened while generating the page. Any console logs will be displayed in the terminal window.
+
+null"
+`;
+
+exports[`Error Overlay for server components createContext called in Server Component should show error when createContext is called in external package 1`] = `
+" 1 of 1 unhandled error
+Server Error
+
+TypeError: createContext only works in Client Components. Add the \\"use client\\" directive at the top of the file to use it. Read more: https://beta.nextjs.org/docs/rendering/server-and-client-components#context
+
+This error happened while generating the page. Any console logs will be displayed in the terminal window.
+
+null"
+`;
diff --git a/test/development/acceptance-app/server-components.test.ts b/test/development/acceptance-app/server-components.test.ts
new file mode 100644
index 000000000000000..47246440498c07c
--- /dev/null
+++ b/test/development/acceptance-app/server-components.test.ts
@@ -0,0 +1,160 @@
+/* eslint-env jest */
+import { sandbox } from './helpers'
+import { createNext, FileRef } from 'e2e-utils'
+import { NextInstance } from 'test/lib/next-modes/base'
+import path from 'path'
+
+describe('Error Overlay for server components', () => {
+ if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
+ it('should skip for react v17', () => {})
+ return
+ }
+
+ let next: NextInstance
+
+ beforeAll(async () => {
+ next = await createNext({
+ files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')),
+ dependencies: {
+ react: 'latest',
+ 'react-dom': 'latest',
+ },
+ skipStart: true,
+ })
+ })
+ afterAll(() => next.destroy())
+
+ describe('createContext called in Server Component', () => {
+ it('should show error when React.createContext is called', async () => {
+ const { session, browser, cleanup } = await sandbox(
+ next,
+ new Map([
+ [
+ 'app/page.js',
+ `
+ import React from 'react'
+ const Context = React.createContext()
+ export default function Page() {
+ return (
+ <>
+
+ Page
+
+ >
+ )
+ }`,
+ ],
+ ])
+ )
+
+ // TODO-APP: currently requires a full reload because moving from a client component to a server component isn't causing a Fast Refresh yet.
+ await browser.refresh()
+
+ expect(await session.hasRedbox(true)).toBe(true)
+ expect(await session.getRedboxSource(true)).toMatchSnapshot()
+ expect(next.cliOutput).toContain(
+ 'createContext only works in Client Components'
+ )
+
+ await cleanup()
+ })
+
+ it('should show error when React.createContext is called in external package', async () => {
+ const { session, browser, cleanup } = await sandbox(
+ next,
+ new Map([
+ [
+ 'node_modules/my-package/index.js',
+ `
+ const React = require('react')
+ module.exports = React.createContext()
+ `,
+ ],
+ [
+ 'node_modules/my-package/package.json',
+ `
+ {
+ "name": "my-package",
+ "version": "0.0.1"
+ }
+ `,
+ ],
+ [
+ 'app/page.js',
+ `
+ import Context from 'my-package'
+ export default function Page() {
+ return (
+ <>
+
+ Page
+
+ >
+ )
+ }`,
+ ],
+ ])
+ )
+
+ // TODO-APP: currently requires a full reload because moving from a client component to a server component isn't causing a Fast Refresh yet.
+ await browser.refresh()
+
+ expect(await session.hasRedbox(true)).toBe(true)
+ expect(await session.getRedboxSource(true)).toMatchSnapshot()
+ expect(next.cliOutput).toContain(
+ 'createContext only works in Client Components'
+ )
+
+ await cleanup()
+ })
+
+ it('should show error when createContext is called in external package', async () => {
+ const { session, browser, cleanup } = await sandbox(
+ next,
+ new Map([
+ [
+ 'node_modules/my-package/index.js',
+ `
+ const { createContext } = require('react')
+ module.exports = createContext()
+ `,
+ ],
+ [
+ 'node_modules/my-package/package.json',
+ `
+ {
+ "name": "my-package",
+ "version": "0.0.1"
+ }
+ `,
+ ],
+ [
+ 'app/page.js',
+ `
+ import Context from 'my-package'
+ export default function Page() {
+ return (
+ <>
+
+ Page
+
+ >
+ )
+ }`,
+ ],
+ ])
+ )
+
+ // TODO-APP: currently requires a full reload because moving from a client component to a server component isn't causing a Fast Refresh yet.
+ await browser.refresh()
+
+ expect(await session.hasRedbox(true)).toBe(true)
+ expect(await session.getRedboxSource(true)).toMatchSnapshot()
+ expect(next.cliOutput).toContain(
+ 'createContext only works in Client Components'
+ )
+
+ await cleanup()
+ })
+ })
+})