diff --git a/packages/next/.storybook/fixtures/errors.ts b/packages/next/.storybook/fixtures/errors.ts index 3c30d0d6c6be..9294c504eac4 100644 --- a/packages/next/.storybook/fixtures/errors.ts +++ b/packages/next/.storybook/fixtures/errors.ts @@ -648,3 +648,18 @@ export const instantClientMathRandomErrors: ReadyRuntimeError[] = [ type: 'runtime', }, ] + +export const instantUnrenderedSegmentErrors: ReadyRuntimeError[] = [ + { + id: 130, + runtime: true, + error: Object.assign( + new Error( + 'Route "/81-instant-wrapper-unrendered-segment/trigger": Could not validate that a segment in your UI has instant navigation.\n\nThis segment was dropped from rendering. Issues that would prevent instant navigation will go undetected.\n\nDropped segment:\n test-app/app/81-instant-wrapper-unrendered-segment/trigger/page.tsx\n\nWays to fix this:\n - Render the dropped segment\n - Set `export const instant = false` on the dropped segment to skip validation\n\nLearn more: https://nextjs.org/docs/messages/unrendered-instant-segment' + ), + { __NEXT_ERROR_CODE: 'E1248' } + ), + frames: () => Promise.resolve([]), + type: 'runtime', + }, +] diff --git a/packages/next/errors.json b/packages/next/errors.json index a0d180d3b8bd..0cb7fef13ce2 100644 --- a/packages/next/errors.json +++ b/packages/next/errors.json @@ -1250,7 +1250,9 @@ "1249": "Route \"%s\": Next.js encountered uncached data during the initial render or a navigation.\\n\\n\\`fetch(...)\\` or \\`connection()\\` accessed outside of \\`\\` prevents the route from being prerendered or the navigation from being instant, leading to a slower user experience.\\n\\nWays to fix this:\\n - Cache the data access with \\`\"use cache\"\\`\\n - Provide a placeholder with \\`\\` around the data access\\n - Set \\`export const instant = false\\` to allow a blocking route\\n\\nLearn more: https://nextjs.org/docs/messages/blocking-route", "1250": "Route \"%s\": Next.js encountered runtime data during the initial render or a navigation.\\n\\n\\`cookies()\\`, \\`headers()\\`, \\`params\\`, or \\`searchParams\\` accessed outside of \\`\\` prevents the route from being prerendered or the navigation from being instant, leading to a slower user experience.\\n\\nWays to fix this:\\n - Use \\`generateStaticParams\\` to make route params static\\n - Provide a placeholder with \\`\\` around the data access\\n - Set \\`export const instant = false\\` to allow a blocking route\\n\\nLearn more: https://nextjs.org/docs/messages/blocking-route", "1251": "Route \"%s\": Next.js encountered runtime data during the initial render or a navigation.\\n\\n\\`cookies()\\`, \\`headers()\\`, \\`params\\`, or \\`searchParams\\` accessed outside of \\`\\` prevents the route from being prerendered or the navigation from being instant, leading to a slower user experience.\\n\\nWays to fix this:\\n - Provide a placeholder with \\`\\` around the data access\\n - Use \\`generateStaticParams\\` to make route params static\\n - Set \\`export const instant = false\\` to allow a blocking route\\n\\nLearn more: https://nextjs.org/docs/messages/blocking-route", - "1252": "\\`experimental.cssChunking: \"graph\"\\` is only supported with Turbopack. Please remove the option or run Next.js with Turbopack in %s.", - "1253": "\\`experimental.cssChunking: \"strict\"\\` is only supported with webpack. Please remove the option or run Next.js with webpack in %s.", - "1254": "\\`experimental.cssChunking: false\\` is only supported with webpack. Please remove the option or run Next.js with webpack in %s." + "1252": "Route \"%s\": Could not validate instant UI because an expected segment was not rendered.", + "1253": "Route \"%s\": Could not validate that a segment in your UI has instant navigation.", + "1254": "\\`experimental.cssChunking: \"graph\"\\` is only supported with Turbopack. Please remove the option or run Next.js with Turbopack in %s.", + "1255": "\\`experimental.cssChunking: \"strict\"\\` is only supported with webpack. Please remove the option or run Next.js with webpack in %s.", + "1256": "\\`experimental.cssChunking: false\\` is only supported with webpack. Please remove the option or run Next.js with webpack in %s." } diff --git a/packages/next/src/next-devtools/dev-overlay/components/code-frame/code-frame-shell.tsx b/packages/next/src/next-devtools/dev-overlay/components/code-frame/code-frame-shell.tsx new file mode 100644 index 000000000000..37f8e595fe1b --- /dev/null +++ b/packages/next/src/next-devtools/dev-overlay/components/code-frame/code-frame-shell.tsx @@ -0,0 +1,43 @@ +import type { ReactNode } from 'react' +import { ExternalIcon } from '../../icons/external' + +type CodeFrameShellProps = { + header: ReactNode + onOpen?: () => void + openLabel?: string + children: ReactNode +} + +export function CodeFrameShell({ + header, + onOpen, + openLabel, + children, +}: CodeFrameShellProps) { + return ( +
+
+ {/* TODO: This is
in `Terminal` component. + Changing now will require multiple test snapshots updates. + Leaving as
as is trivial and does not affect the UI. + Change when the new redbox matcher `toDisplayRedbox` is used. */} +

+ {header} + {onOpen && ( + + )} +

+
+
+        
{children}
+
+
+ ) +} diff --git a/packages/next/src/next-devtools/dev-overlay/components/code-frame/code-frame.tsx b/packages/next/src/next-devtools/dev-overlay/components/code-frame/code-frame.tsx index cb344848bac5..d0435dac5e3a 100644 --- a/packages/next/src/next-devtools/dev-overlay/components/code-frame/code-frame.tsx +++ b/packages/next/src/next-devtools/dev-overlay/components/code-frame/code-frame.tsx @@ -2,8 +2,8 @@ import { useMemo } from 'react' import { HotlinkedText } from '../hot-linked-text' import { getStackFrameFile, type StackFrame } from '../../../shared/stack-frame' import { useOpenInEditor } from '../../utils/use-open-in-editor' -import { ExternalIcon } from '../../icons/external' import { FileIcon } from '../../icons/file' +import { CodeFrameShell } from './code-frame-shell' import { formatCodeFrame, groupCodeFrameLines, @@ -35,14 +35,9 @@ export function CodeFrame({ stackFrame, codeFrame }: CodeFrameProps) { const fileExtension = stackFrame?.file?.split('.').pop() return ( -
-
- {/* TODO: This is
in `Terminal` component. - Changing now will require multiple test snapshots updates. - Leaving as
as is trivial and does not affect the UI. - Change when the new redbox matcher `toDisplayRedbox` is used. - */} -

+ @@ -50,54 +45,45 @@ export function CodeFrame({ stackFrame, codeFrame }: CodeFrameProps) { {getStackFrameFile(stackFrame)} @{' '} - -

-
-
-        
- {parsedLineStates.map(({ line, parsedLine }, lineIndex) => { - const { lineNumber, isErroredLine } = parsedLine - - const lineNumberProps: Record = {} - if (lineNumber) { - lineNumberProps['data-nextjs-codeframe-line'] = lineNumber - } - if (isErroredLine) { - lineNumberProps['data-nextjs-codeframe-line--errored'] = true - } - - return ( -
- {line.map((entry, entryIndex) => ( - - {entry.content} - - ))} -
- ) - })} -
-
-
+ + } + onOpen={open} + > + {parsedLineStates.map(({ line, parsedLine }, lineIndex) => { + const { lineNumber, isErroredLine } = parsedLine + + const lineNumberProps: Record = {} + if (lineNumber) { + lineNumberProps['data-nextjs-codeframe-line'] = lineNumber + } + if (isErroredLine) { + lineNumberProps['data-nextjs-codeframe-line--errored'] = true + } + + return ( +
+ {line.map((entry, entryIndex) => ( + + {entry.content} + + ))} +
+ ) + })} + ) } @@ -218,7 +204,7 @@ export const CODE_FRAME_STYLES = ` } [data-nextjs-codeframe-line] > span:first-child { - color: var(--color-gray-alpha-700) !important; + color: var(--color-gray-alpha-500) !important; } [data-nextjs-codeframe-line][data-nextjs-codeframe-line--errored="true"] diff --git a/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance-data.ts b/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance-data.ts index 1d6207dcb489..8eee1704cd4f 100644 --- a/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance-data.ts +++ b/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance-data.ts @@ -10,6 +10,8 @@ export type FixCardGroup = | 'client' | 'defer' | 'measure' + | 'ignore' + | 'render' export type FixCardIcon = | 'align-left' @@ -18,6 +20,7 @@ export type FixCardIcon = | 'layout' | 'octagon' | 'pointer-click' + | 'minus-circle' | 'server-stack' | 'timer' | 'zap' @@ -35,6 +38,8 @@ export const FIX_CARD_GROUPS: Record< client: { label: 'Client', color: 'amber', icon: 'layout' }, defer: { label: 'Defer', color: 'amber', icon: 'pointer-click' }, measure: { label: 'Measure', color: 'gray', icon: 'timer' }, + ignore: { label: 'Ignore', color: 'red', icon: 'minus-circle' }, + render: { label: 'Render', color: 'gray', icon: 'layout' }, } export type FixCard = { @@ -146,6 +151,47 @@ const dynamicCards: FixCard[] = [ }, ] +// ── Unrendered-segment cards ────────────────────── + +const unrenderedSegmentCards: FixCard[] = [ + { + id: 'render-the-dropped-segment', + title: 'Render the dropped segment', + group: 'render', + link: 'https://nextjs.org/docs/messages/unrendered-instant-segment#render-the-dropped-segment', + snippets: [ + { + text: 'function Layout({ children }) {', + parts: [ + { text: 'function Layout({ ' }, + { text: 'children', highlight: true }, + { text: ' }) {' }, + ], + }, + { + text: ' return <>