diff --git a/errors/large-page-data.md b/errors/large-page-data.md new file mode 100644 index 0000000000000..62600879b4415 --- /dev/null +++ b/errors/large-page-data.md @@ -0,0 +1,13 @@ +# Large Page Data + +#### Why This Error Occurred + +One of your pages includes a large amount of page data (>= 128KB). This can negatively impact performance since page data must be parsed by the client before the page is hydrated. + +#### Possible Ways to Fix It + +Reduce the amount of data returned from `getStaticProps`, `getServerSideProps`, or `getInitialProps` to only the essential data to render the page. + +### Useful Links + +- [Data Fetching Documentation](https://nextjs.org/docs/basic-features/data-fetching) diff --git a/errors/manifest.json b/errors/manifest.json index d81f091664c8a..0145ee2569fb4 100644 --- a/errors/manifest.json +++ b/errors/manifest.json @@ -20,6 +20,10 @@ "title": "custom-document-image-import", "path": "/errors/custom-document-image-import.md" }, + { + "title": "large-page-data", + "path": "/errors/large-page-data.md" + }, { "title": "404-get-initial-props", "path": "/errors/404-get-initial-props.md" diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index c6093d7760188..a1c855bef1d70 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -801,6 +801,19 @@ export class NextScript extends Component { const { __NEXT_DATA__ } = context try { const data = JSON.stringify(__NEXT_DATA__) + + if (process.env.NODE_ENV === 'development') { + const bytes = Buffer.from(data).byteLength + const prettyBytes = require('../lib/pretty-bytes').default + if (bytes > 128 * 1000) { + console.warn( + `Warning: data for page "${__NEXT_DATA__.page}" is ${prettyBytes( + bytes + )}, this amount of data can reduce performance.\nSee more info here: https://nextjs.org/docs/messages/large-page-data` + ) + } + } + return htmlEscapeJsonString(data) } catch (err) { if (isError(err) && err.message.indexOf('circular structure')) { diff --git a/test/e2e/prerender.test.ts b/test/e2e/prerender.test.ts index eec4b728b61ce..4f9025076f32f 100644 --- a/test/e2e/prerender.test.ts +++ b/test/e2e/prerender.test.ts @@ -137,6 +137,11 @@ describe('Prerender', () => { initialRevalidateSeconds: false, srcRoute: '/catchall-optional/[[...slug]]', }, + '/large-page-data': { + dataRoute: `/_next/data/${next.buildId}/large-page-data.json`, + initialRevalidateSeconds: false, + srcRoute: null, + }, '/another': { dataRoute: `/_next/data/${next.buildId}/another.json`, initialRevalidateSeconds: 1, @@ -880,6 +885,14 @@ describe('Prerender', () => { }) if ((global as any).isNextDev) { + it('should show warning when large amount of page data is returned', async () => { + await renderViaHTTP(next.url, '/large-page-data') + await check( + () => next.cliOutput, + /Warning: data for page "\/large-page-data" is 128 kB, this amount of data can reduce performance/ + ) + }) + it('should not show warning from url prop being returned', async () => { const urlPropPage = 'pages/url-prop.js' await next.patchFile( @@ -1386,6 +1399,12 @@ describe('Prerender', () => { lang: 'lang', }, }, + { + dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex( + next.buildId + )}\\/large-page-data.json$`, + page: '/large-page-data', + }, { namedDataRouteRegex: `^/_next/data/${escapeRegex( next.buildId diff --git a/test/e2e/prerender/pages/large-page-data.js b/test/e2e/prerender/pages/large-page-data.js new file mode 100644 index 0000000000000..5293c5669cd50 --- /dev/null +++ b/test/e2e/prerender/pages/large-page-data.js @@ -0,0 +1,26 @@ +import React from 'react' +import Link from 'next/link' + +export async function getStaticProps({ params }) { + return { + props: { + lotsOfData: new Array(128 * 1000).fill('a').join(''), + }, + revalidate: false, + } +} + +export default (props) => { + return ( + <> +

lots of data returned

+ + to home + +
+ + to another + + + ) +}