diff --git a/docs/advanced-features/codemods.md b/docs/advanced-features/codemods.md
index 5c42d4551bb9..d20824316e58 100644
--- a/docs/advanced-features/codemods.md
+++ b/docs/advanced-features/codemods.md
@@ -17,13 +17,42 @@ Codemods are transformations that run on your codebase programmatically. This al
- `--dry` Do a dry-run, no code will be edited
- `--print` Prints the changed output for comparison
+## Next.js 10
+
+### `add-missing-react-import`
+
+Transforms files that do not import `React` to include the import in order for the new [React JSX transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) to work.
+
+For example:
+
+```jsx
+// my-component.js
+export default class Home extends React.Component {
+ render() {
+ return
Hello World
+ }
+}
+```
+
+Transforms into:
+
+```jsx
+// my-component.js
+import React from 'react'
+export default class Home extends React.Component {
+ render() {
+ return Hello World
+ }
+}
+```
+
## Next.js 9
### `name-default-component`
Transforms anonymous components into named components to make sure they work with [Fast Refresh](https://nextjs.org/blog/next-9-4#fast-refresh).
-For example
+For example:
```jsx
// my-component.js
diff --git a/docs/upgrading.md b/docs/upgrading.md
index 4c1c029fc663..80762f8b7124 100644
--- a/docs/upgrading.md
+++ b/docs/upgrading.md
@@ -4,6 +4,10 @@ description: Learn how to upgrade Next.js.
# Upgrade Guide
+## React 16 to 17
+
+React 17 introduced a new [JSX Transform](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) that brings a long-time Next.js feature to the wider React ecosystem: Not having to `import React from 'react'` when using JSX. When using React 17 Next.js will automatically use the new transform. This transform does not make the `React` variable global, which was an unintended side-effect of the previous Next.js implementation. A [codemod is available](/docs/advanced-features/codemods#add-missing-react-import) to automatically fix cases where you accidentally used `React` without importing it.
+
## Upgrading from version 9 to 10
There were no breaking changes between version 9 and 10.
diff --git a/packages/next-codemod/bin/cli.ts b/packages/next-codemod/bin/cli.ts
index c3552bc79740..76278a7bafaf 100644
--- a/packages/next-codemod/bin/cli.ts
+++ b/packages/next-codemod/bin/cli.ts
@@ -97,6 +97,11 @@ const TRANSFORMER_INQUIRER_CHOICES = [
'name-default-component: Transforms anonymous components into named components to make sure they work with Fast Refresh',
value: 'name-default-component',
},
+ {
+ name:
+ 'add-missing-react-import: Transforms files that do not import `React` to include the import in order for the new React JSX transform',
+ value: 'add-missing-react-import',
+ },
{
name:
'withamp-to-config: Transforms the withAmp HOC into Next.js 9 page configuration',
diff --git a/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/class-component.input.js b/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/class-component.input.js
new file mode 100644
index 000000000000..f78adb630b08
--- /dev/null
+++ b/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/class-component.input.js
@@ -0,0 +1,5 @@
+export default class Home extends React.Component {
+ render() {
+ return Hello World
+ }
+}
\ No newline at end of file
diff --git a/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/class-component.output.js b/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/class-component.output.js
new file mode 100644
index 000000000000..06c84a885d99
--- /dev/null
+++ b/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/class-component.output.js
@@ -0,0 +1,6 @@
+import React from 'react'
+export default class Home extends React.Component {
+ render() {
+ return Hello World
+ }
+}
\ No newline at end of file
diff --git a/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/missing-react-import-in-component.input.js b/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/missing-react-import-in-component.input.js
new file mode 100644
index 000000000000..5ba85aa61068
--- /dev/null
+++ b/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/missing-react-import-in-component.input.js
@@ -0,0 +1,16 @@
+import { Children, isValidElement } from 'react';
+
+function Heading(props) {
+ const { component, className, children, ...rest } = props;
+ return React.cloneElement(
+ component,
+ {
+ className: [className, component.props.className || ''].join(' '),
+ ...rest
+ },
+ children
+ );
+}
+
+
+export default Heading;
diff --git a/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/missing-react-import-in-component.output.js b/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/missing-react-import-in-component.output.js
new file mode 100644
index 000000000000..7b15a0ad4b14
--- /dev/null
+++ b/packages/next-codemod/transforms/__testfixtures__/add-missing-react-import/missing-react-import-in-component.output.js
@@ -0,0 +1,16 @@
+import React, { Children, isValidElement } from 'react';
+
+function Heading(props) {
+ const { component, className, children, ...rest } = props;
+ return React.cloneElement(
+ component,
+ {
+ className: [className, component.props.className || ''].join(' '),
+ ...rest
+ },
+ children
+ );
+}
+
+
+export default Heading;
diff --git a/packages/next-codemod/transforms/__tests__/add-missing-react-import.js b/packages/next-codemod/transforms/__tests__/add-missing-react-import.js
new file mode 100644
index 000000000000..11342ad49e61
--- /dev/null
+++ b/packages/next-codemod/transforms/__tests__/add-missing-react-import.js
@@ -0,0 +1,16 @@
+/* global jest */
+jest.autoMockOff()
+const defineTest = require('jscodeshift/dist/testUtils').defineTest
+
+const fixtures = [
+ 'missing-react-import-in-component'
+]
+
+for (const fixture of fixtures) {
+ defineTest(
+ __dirname,
+ 'add-missing-react-import',
+ null,
+ `add-missing-react-import/${fixture}`
+ )
+}
diff --git a/packages/next-codemod/transforms/add-missing-react-import.ts b/packages/next-codemod/transforms/add-missing-react-import.ts
new file mode 100644
index 000000000000..f545778001d8
--- /dev/null
+++ b/packages/next-codemod/transforms/add-missing-react-import.ts
@@ -0,0 +1,77 @@
+function addReactImport(j, root) {
+ // We create an import specifier, this is the value of an import, eg:
+ // import React from 'react'
+ // The specifier would be `React`
+ const ReactDefaultSpecifier = j.importDefaultSpecifier(j.identifier('React'))
+
+ // Check if this file is already importing `react`
+ // so that we can attach `React` to the existing import instead of creating a new `import` node
+ const originalReactImport = root.find(j.ImportDeclaration, {
+ source: {
+ value: 'react',
+ },
+ })
+ if (originalReactImport.length > 0) {
+ // Check if `React` is already imported. In that case we don't have to do anything
+ if (originalReactImport.find(j.ImportDefaultSpecifier).length > 0) {
+ return
+ }
+
+ // Attach `React` to the existing `react` import node
+ originalReactImport.forEach((node) => {
+ node.value.specifiers.unshift(ReactDefaultSpecifier)
+ })
+ return
+ }
+
+ // Create import node
+ // import React from 'react'
+ const ReactImport = j.importDeclaration(
+ [ReactDefaultSpecifier],
+ j.stringLiteral('react')
+ )
+
+ // Find the Program, this is the top level AST node
+ const Program = root.find(j.Program)
+ // Attach the import at the top of the body
+ Program.forEach((node) => {
+ node.value.body.unshift(ReactImport)
+ })
+}
+
+export default function transformer(file, api, options) {
+ const j = api.jscodeshift
+ const root = j(file.source)
+
+ const hasReactImport = (r) => {
+ return (
+ r.find(j.ImportDefaultSpecifier, {
+ local: {
+ type: 'Identifier',
+ name: 'React',
+ },
+ }).length > 0
+ )
+ }
+
+ const hasReactVariableUsage = (r) => {
+ return (
+ r.find(j.MemberExpression, {
+ object: {
+ type: 'Identifier',
+ name: 'React',
+ },
+ }).length > 0
+ )
+ }
+
+ if (hasReactImport(root)) {
+ return
+ }
+
+ if (hasReactVariableUsage(root)) {
+ addReactImport(j, root)
+ }
+
+ return root.toSource(options)
+}