Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: vercel/next.js
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 5f3a55d813bc7941cc853fe92f2bde6d98ccd9bd
Choose a base ref
..
head repository: vercel/next.js
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a3be56dd48a7630a3b65dc3cb8d2349284b4cf23
Choose a head ref
Showing with 241 additions and 458 deletions.
  1. +18 −1 docs/advanced-features/using-mdx.md
  2. +13 −9 docs/api-reference/next/image.md
  3. +29 −1 docs/basic-features/data-fetching/get-static-props.md
  4. +15 −12 docs/guides/building-forms.md
  5. +2 −0 errors/page-without-valid-component.md
  6. +1 −1 packages/create-next-app/templates/typescript/gitignore
  7. +3 −26 packages/next/build/webpack-config.ts
  8. +1 −0 packages/next/build/webpack/loaders/next-middleware-ssr-loader/render.ts
  9. +0 −1 packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts
  10. +2 −1 packages/next/client/dev/error-overlay/hot-dev-client.js
  11. +3 −1 packages/next/client/index.tsx
  12. +4 −5 packages/next/client/route-announcer.tsx
  13. +1 −1 packages/next/compiled/@next/react-dev-overlay/client.js
  14. +0 −2 packages/next/export/index.ts
  15. +0 −9 packages/next/export/worker.ts
  16. +0 −2 packages/next/pages/_document.tsx
  17. +0 −2 packages/next/server/base-server.ts
  18. +0 −2 packages/next/server/config-shared.ts
  19. +21 −2 packages/next/server/config.ts
  20. +3 −1 packages/next/server/dev/next-dev-server.ts
  21. +8 −2 packages/next/server/load-components.ts
  22. +1 −5 packages/next/server/next-server.ts
  23. +1 −6 packages/next/server/render.tsx
  24. +0 −108 packages/next/shared/lib/post-process.ts
  25. +0 −1 packages/next/shared/lib/utils.ts
  26. +12 −0 packages/react-dev-overlay/src/internal/container/FullRefreshWarning.tsx
  27. +8 −1 test/integration/client-navigation-a11y/test/index.test.js
  28. +0 −4 test/integration/image-optimization/next.config.js
  29. +0 −25 test/integration/image-optimization/pages/index.js
  30. +0 −30 test/integration/image-optimization/pages/stars.js
  31. +0 −18 test/integration/image-optimization/pages/static-head.js
  32. +0 −12 test/integration/image-optimization/pages/with-querystring.js
  33. +0 −111 test/integration/image-optimization/test/index.test.js
  34. +1 −0 test/integration/react-18/app/next.config.js
  35. +77 −51 test/integration/react-18/test/index.test.js
  36. +16 −0 test/integration/react-18/test/strict-mode.js
  37. +0 −4 test/integration/react-18/test/with-react-18.js
  38. +1 −1 test/integration/react-streaming-and-server-components/app/next.config.js
19 changes: 18 additions & 1 deletion docs/advanced-features/using-mdx.md
Original file line number Diff line number Diff line change
@@ -155,7 +155,22 @@ The above generates the following `HTML`:
</ul>
```

When you want to style your own elements to give a custom feel to your website or application, you can pass in shortcodes. These are your own custom components that map to `HTML` elements. To do this you use the `MDXProvider` and pass a components object as a prop. Each object key in the components object maps to a `HTML` element name. You also need to specify `providerImportSource: "@mdx-js/react"` in `next.config.js`.
When you want to style your own elements to give a custom feel to your website or application, you can pass in shortcodes. These are your own custom components that map to `HTML` elements. To do this you use the `MDXProvider` and pass a components object as a prop. Each object key in the components object maps to a `HTML` element name.

To enable you need to specify `providerImportSource: "@mdx-js/react"` in `next.config.js`.

```js
// next.config.js

const withMDX = require('@next/mdx')({
// ...
options: {
providerImportSource: '@mdx-js/react',
},
})
```

Then setup the provider in your page

```jsx
// pages/index.js
@@ -186,6 +201,8 @@ export default function Post(props) {
}
```

If you use it across the site you may want to add the provider to `_app.js` so all MDX pages pick up the custom element config.

## Helpful Links

- [MDX](https://mdxjs.com)
22 changes: 13 additions & 9 deletions docs/api-reference/next/image.md
Original file line number Diff line number Diff line change
@@ -252,19 +252,23 @@ const Example = () => (
import Image from 'next/image'
import React from 'react'

const Container = React.forwardRef((props, ref) =>
<div ref={ref} style={{ overflowX: 'scroll', width: '500px' }}>
{props.children}
</div>
const Container = React.forwardRef((props, ref) => {
return (
<div ref={ref} style={{ overflowX: 'scroll', width: '500px' }}>
{props.children}
</div>
)
})

const Example = () => {
const lazyRoot = React.useRef(null)

return (<Container ref={lazyRoot}>
<Image lazyRoot={lazyRoot} src="/one.jpg" width="500" height="500" />
<Image lazyRoot={lazyRoot} src="/two.jpg" width="500" height="500" />
</Container>)
return (
<Container ref={lazyRoot}>
<Image lazyRoot={lazyRoot} src="/one.jpg" width="500" height="500" />
<Image lazyRoot={lazyRoot} src="/two.jpg" width="500" height="500" />
</Container>
)
}
```

@@ -319,7 +323,7 @@ module.exports = {
The following Image Optimization cloud providers are included:

- Default: Works automatically with `next dev`, `next start`, or a custom server
- [Vercel](https://vercel.com): Works automatically when you deploy on Vercel, no configuration necessary. [Learn more](https://vercel.com/docs/next.js/image-optimization)
- [Vercel](https://vercel.com): Works automatically when you deploy on Vercel, no configuration necessary. [Learn more](https://vercel.com/docs/concepts/image-optimization)
- [Imgix](https://www.imgix.com): `loader: 'imgix'`
- [Cloudinary](https://cloudinary.com): `loader: 'cloudinary'`
- [Akamai](https://www.akamai.com): `loader: 'akamai'`
30 changes: 29 additions & 1 deletion docs/basic-features/data-fetching/get-static-props.md
Original file line number Diff line number Diff line change
@@ -72,7 +72,35 @@ As `getStaticProps` runs only on the server-side, it will never run on the clien

This means that instead of fetching an **API route** from `getStaticProps` (that itself fetches data from an external source), you can write the server-side code directly in `getStaticProps`.

Take the following example. An API route is used to fetch some data from a CMS. That API route is then called directly from `getStaticProps`. This produces an additional call, reducing performance. Instead, the logic for fetching the data from the CMS can be moved to `getStaticProps`.
Take the following example. An API route is used to fetch some data from a CMS. That API route is then called directly from `getStaticProps`. This produces an additional call, reducing performance. Instead, the logic for fetching the data from the CMS can be shared by using a `lib/` directory. Then it can be shared with `getStaticProps`.

```jsx
// lib/fetch-posts.js

// The following function is shared
// with getStaticProps and API routes
// from a `lib/` directory
export async function loadPosts() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts/')
const data = await res.json()

return data
}

// pages/blog.js
import { loadPosts } from '../lib/load-posts'

// This function runs only on the server side
export async function getStaticProps() {
// Instead of fetching your `/api` route you can call the same
// function directly in `getStaticProps`
const posts = await loadPosts()

// Props returned will be passed to the page component
return { props: { posts } }
}
```

Alternatively, if you are **not** using API routes to fetch data, then the [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API _can_ be used directly in `getStaticProps` to fetch data.

27 changes: 15 additions & 12 deletions docs/guides/building-forms.md
Original file line number Diff line number Diff line change
@@ -33,10 +33,10 @@ The front-end looks like this:
The HTML `<form>` tag acts as a container for different `<input>` elements like `text` field and submit `button`. Let's study each of these elements:

- `action`: An attribute that specifies where the form data is sent when the form is submitted. It's generally a URL (an absolute URL or a relative URL).
- `method`: Specifies the HTTP method, i.e., `GET` or `POST` used to send data while submitting the form.
- `<label>`: An element that defines the label for other form elements. Label aids accessibility, especially for screen readers.
- `method`: Specifies the [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods), i.e., `GET` or `POST` used to send data while submitting the form.
- `<label>`: An element that defines the label for other form elements. Labels aid accessibility, especially for screen readers.
- `<input>`: The form element that is widely used to structure the form fields. It depends significantly on the value of the `type` attribute. Input types can be `text`, `checkbox`, `email`, `radio`, and more.
- `<button>`: It represents a clickable button that's used to submit the form data.
- `<button>`: Represents a clickable button that's used to submit the form data.

### Form Validation

@@ -55,11 +55,11 @@ Client-side validation is further categorized as:
### Built-in Form Validation Using `required`, `type`, `minLength`, `maxLength`

- `required`: Specifies which fields must be filled before submitting the form.
- `type`: Tells whether the data should be a number, email id, text string, etc.
- `minLength`: Decides the minimum length for the text data string.
- `maxLength`: Decides the maximum length for the text data string.
- `type`: Specifies the data's type (i.e a number, email address, string, etc).
- `minLength`: Specifies minimum length for the text data string.
- `maxLength`: Specifies maximum length for the text data string.

The following example shows using these attributes:
So, a form using this attributes may look like:

```html
<!-- HTML Form with Built-in Validation -->
@@ -85,7 +85,7 @@ With these validation checks in place, when a user tries to submit an empty fiel

### JavaScript-based Form Validation

Form Validation is important to ensure that a user has submitted appropriate data. JavaScript offers an additional level of validation along with HTML native form attributes on the client side. Developers generally prefer validating form data through JavaScript because its data processing is faster when compared to server-side validation.
Form Validation is important to ensure that a user has submitted the correct data, in a correct format. JavaScript offers an additional level of validation along with HTML native form attributes on the client side. Developers generally prefer validating form data through JavaScript because its data processing is faster when compared to server-side validation, however front-end validation may be less secure in some scenarios as a malicious user could always send malformed data to your server.

The following example shows using JavaScript to validate a form:

@@ -143,7 +143,7 @@ The below example shows using the `pattern` attribute on an `input` element:
</form>
```

The password form field must only contain digits (0 to 9) and lowercase alphabets (a to z). No other characters (#,$,&, etc.) are allowed. The rule in RegEx is written as `[a-z]{1,15}`.
The password form field must only contain digits (0 to 9), lowercase alphabets (a to z) and it must be no more than 15 characters in length. No other characters (#,$,&, etc.) are allowed. The rule in RegEx is written as `[a-z0-9]{1,15}`.

![form-validate-regex](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/form-validate-regex.jpg)

@@ -180,13 +180,16 @@ export default function handler(req, res) {
// in the command line where next.js app is running.
console.log('body: ', body)

// Both of these are required.
// Guard clause checks for first and last name,
// and returns early if they are not found
if (!body.first || !body.last) {
return res.json({ data: 'First or last name not found' })
// Sends a HTTP bad request error code
return res.status(400).json({ data: 'First or last name not found' })
}

// Found the name.
res.json({ data: `${body.first} ${body.last}` })
// Sends a HTTP success code
res.status(200).json({ data: `${body.first} ${body.last}` })
}
```

2 changes: 2 additions & 0 deletions errors/page-without-valid-component.md
Original file line number Diff line number Diff line change
@@ -14,3 +14,5 @@ For each, you'll want to check if the file is meant to be a page.
If the file is not meant to be a page, and instead, is a shared component or file, move the file to a different folder like `components` or `lib`.

If the file is meant to be a page, double check you have an `export default` with the React Component instead of an `export`. If you're already using `export default`, make sure the returned value is a valid React Component.

If you need to support a different file extension for a page component (such as `.mdx`) or would like to include non-page files in the `pages` directory, configure [Custom Page Extensions](https://nextjs.org/docs/api-reference/next.config.js/custom-page-extensions) in the `next.config.js`.
2 changes: 1 addition & 1 deletion packages/create-next-app/templates/typescript/gitignore
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log**
.pnpm-debug.log*

# local env files
.env.local
29 changes: 3 additions & 26 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@ import ReactRefreshWebpackPlugin from 'next/dist/compiled/@next/react-refresh-ut
import chalk from 'next/dist/compiled/chalk'
import crypto from 'crypto'
import { stringify } from 'querystring'
import semver from 'next/dist/compiled/semver'
import { webpack } from 'next/dist/compiled/webpack/webpack'
import type { webpack5 } from 'next/dist/compiled/webpack/webpack'
import path, { join as pathJoin, relative as relativePath } from 'path'
@@ -15,7 +14,6 @@ import {
MIDDLEWARE_ROUTE,
} from '../lib/constants'
import { fileExists } from '../lib/file-exists'
import { getPackageVersion } from '../lib/get-package-version'
import { CustomRoutes } from '../lib/load-custom-routes.js'
import {
CLIENT_STATIC_FILES_RUNTIME_AMP,
@@ -52,6 +50,7 @@ import type { Span } from '../trace'
import { getRawPageExtensions } from './utils'
import browserslist from 'next/dist/compiled/browserslist'
import loadJsConfig from './load-jsconfig'
import { shouldUseReactRoot } from '../server/config'

const watchOptions = Object.freeze({
aggregateTimeout: 5,
@@ -337,21 +336,7 @@ export default async function getBaseWebpackConfig(
rewrites.afterFiles.length > 0 ||
rewrites.fallback.length > 0
const hasReactRefresh: boolean = dev && !isServer
const reactDomVersion = await getPackageVersion({
cwd: dir,
name: 'react-dom',
})
const isReactExperimental = Boolean(
reactDomVersion && /0\.0\.0-experimental/.test(reactDomVersion)
)
const hasReact18: boolean =
Boolean(reactDomVersion) &&
(semver.gte(reactDomVersion!, '18.0.0') ||
semver.coerce(reactDomVersion)?.version === '18.0.0')

const hasReactRoot: boolean =
config.experimental.reactRoot || hasReact18 || isReactExperimental

const hasReactRoot = shouldUseReactRoot()
const runtime = config.experimental.runtime

// Make sure reactRoot is enabled when react 18 is detected
@@ -360,11 +345,7 @@ export default async function getBaseWebpackConfig(
}

// Only inform during one of the builds
if (
!isServer &&
config.experimental.reactRoot &&
!(hasReact18 || isReactExperimental)
) {
if (!isServer && config.experimental.reactRoot && !hasReactRoot) {
// It's fine to only mention React 18 here as we don't recommend people to try experimental.
Log.warn('You have to use React 18 to use `experimental.reactRoot`.')
}
@@ -1365,9 +1346,6 @@ export default async function getBaseWebpackConfig(
'process.env.__NEXT_OPTIMIZE_FONTS': JSON.stringify(
config.optimizeFonts && !dev
),
'process.env.__NEXT_OPTIMIZE_IMAGES': JSON.stringify(
config.experimental.optimizeImages
),
'process.env.__NEXT_OPTIMIZE_CSS': JSON.stringify(
config.experimental.optimizeCss && !dev
),
@@ -1630,7 +1608,6 @@ export default async function getBaseWebpackConfig(
reactStrictMode: config.reactStrictMode,
reactMode: config.experimental.reactMode,
optimizeFonts: config.optimizeFonts,
optimizeImages: config.experimental.optimizeImages,
optimizeCss: config.experimental.optimizeCss,
scrollRestoration: config.experimental.scrollRestoration,
basePath: config.basePath,
Original file line number Diff line number Diff line change
@@ -72,6 +72,7 @@ export function getRender({
webServerConfig: {
extendRenderOpts: {
buildId,
reactRoot: true,
runtime: 'edge',
supportsDynamicHTML: true,
disableOptimizedLoading: true,
Original file line number Diff line number Diff line change
@@ -191,7 +191,6 @@ export function getPageHandler(ctx: ServerlessHandlerCtx) {
locale: detectedLocale,
defaultLocale,
domainLocales: i18n?.domains,
optimizeImages: process.env.__NEXT_OPTIMIZE_IMAGES,
optimizeCss: process.env.__NEXT_OPTIMIZE_CSS,
crossOrigin: process.env.__NEXT_CROSS_ORIGIN,
},
3 changes: 2 additions & 1 deletion packages/next/client/dev/error-overlay/hot-dev-client.js
Original file line number Diff line number Diff line change
@@ -350,5 +350,6 @@ function hasAlreadyWarnedAboutFullRefresh() {
}

function clearFullRefreshStorage() {
sessionStorage.removeItem(FULL_REFRESH_STORAGE_KEY)
if (sessionStorage.getItem(FULL_REFRESH_STORAGE_KEY) !== 'ignore')
sessionStorage.removeItem(FULL_REFRESH_STORAGE_KEY)
}
4 changes: 3 additions & 1 deletion packages/next/client/index.tsx
Original file line number Diff line number Diff line change
@@ -775,9 +775,11 @@ if (process.env.__NEXT_RSC) {
serialized?: string
_fresh?: boolean
}) => {
React.useEffect(() => {
rscCache.delete(cacheKey)
})
const response = useServerResponse(cacheKey, serialized)
const root = response.readRoot()
rscCache.delete(cacheKey)
return root
}

9 changes: 4 additions & 5 deletions packages/next/client/route-announcer.tsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ export function RouteAnnouncer() {

// Only announce the path change, but not for the first load because screen
// reader will do that automatically.
const initialPathLoaded = React.useRef(false)
const previouslyLoadedPath = React.useRef(asPath)

// Every time the path changes, announce the new page’s title following this
// priority: first the document title (from head), otherwise the first h1, or
@@ -17,10 +17,9 @@ export function RouteAnnouncer() {
// https://www.gatsbyjs.com/blog/2019-07-11-user-testing-accessible-client-routing/
React.useEffect(
() => {
if (!initialPathLoaded.current) {
initialPathLoaded.current = true
return
}
// If the path hasn't change, we do nothing.
if (previouslyLoadedPath.current === asPath) return
previouslyLoadedPath.current = asPath

if (document.title) {
setRouteAnnouncement(document.title)
2 changes: 1 addition & 1 deletion packages/next/compiled/@next/react-dev-overlay/client.js

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions packages/next/export/index.ts
Original file line number Diff line number Diff line change
@@ -385,7 +385,6 @@ export default async function exportApp(
crossOrigin: nextConfig.crossOrigin,
optimizeCss: nextConfig.experimental.optimizeCss,
optimizeFonts: nextConfig.optimizeFonts,
optimizeImages: nextConfig.experimental.optimizeImages,
reactRoot: nextConfig.experimental.reactRoot || false,
}

@@ -583,7 +582,6 @@ export default async function exportApp(
buildExport: options.buildExport,
serverless: isTargetLikeServerless(nextConfig.target),
optimizeFonts: nextConfig.optimizeFonts,
optimizeImages: nextConfig.experimental.optimizeImages,
optimizeCss: nextConfig.experimental.optimizeCss,
disableOptimizedLoading:
nextConfig.experimental.disableOptimizedLoading,
9 changes: 0 additions & 9 deletions packages/next/export/worker.ts
Original file line number Diff line number Diff line change
@@ -55,7 +55,6 @@ interface ExportPageInput {
subFolders?: boolean
serverless: boolean
optimizeFonts: boolean
optimizeImages?: boolean
optimizeCss: any
disableOptimizedLoading: any
parentSpanId: any
@@ -77,7 +76,6 @@ interface RenderOpts {
ampValidatorPath?: string
ampSkipValidation?: boolean
optimizeFonts?: boolean
optimizeImages?: boolean
disableOptimizedLoading?: boolean
optimizeCss?: any
fontManifest?: FontManifest
@@ -105,7 +103,6 @@ export default async function exportPage({
subFolders,
serverless,
optimizeFonts,
optimizeImages,
optimizeCss,
disableOptimizedLoading,
httpAgentOptions,
@@ -304,8 +301,6 @@ export default async function exportPage({
/// @ts-ignore
optimizeFonts,
/// @ts-ignore
optimizeImages,
/// @ts-ignore
optimizeCss,
disableOptimizedLoading,
distDir,
@@ -367,9 +362,6 @@ export default async function exportPage({
if (optimizeFonts) {
process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(true)
}
if (optimizeImages) {
process.env.__NEXT_OPTIMIZE_IMAGES = JSON.stringify(true)
}
if (optimizeCss) {
process.env.__NEXT_OPTIMIZE_CSS = JSON.stringify(true)
}
@@ -379,7 +371,6 @@ export default async function exportPage({
ampPath: renderAmpPath,
params,
optimizeFonts,
optimizeImages,
optimizeCss,
disableOptimizedLoading,
fontManifest: optimizeFonts
Loading