@@ -402,6 +480,8 @@ export const MDXComponents = {
pre: CodeBlock,
CodeDiagram,
ConsoleBlock,
+ ConsoleBlockMulti,
+ ConsoleLogLine,
DeepDive: (props: {
children: React.ReactNode;
title: string;
@@ -422,16 +502,23 @@ export const MDXComponents = {
IllustrationBlock,
Intro,
InlineToc,
+ LanguageList,
LearnMore,
Math,
MathI,
Note,
Canary,
+ CanaryBadge,
+ NextMajor,
+ NextMajorBadge,
+ RSC,
+ RSCBadge,
PackageImport,
ReadBlogPost,
Recap,
Recipes,
Sandpack,
+ SandpackWithHTMLOutput,
TeamMember,
TerminalBlock,
YouWillLearn,
@@ -441,6 +528,7 @@ export const MDXComponents = {
Solution,
CodeStep,
YouTubeIframe,
+ ErrorDecoder,
};
for (let key in MDXComponents) {
diff --git a/src/components/MDX/Sandpack/CustomPreset.tsx b/src/components/MDX/Sandpack/CustomPreset.tsx
index c133e2a83..7d6e566d2 100644
--- a/src/components/MDX/Sandpack/CustomPreset.tsx
+++ b/src/components/MDX/Sandpack/CustomPreset.tsx
@@ -28,6 +28,7 @@ export const CustomPreset = memo(function CustomPreset({
const {activeFile} = sandpack;
const lineCountRef = useRef<{[key: string]: number}>({});
if (!lineCountRef.current[activeFile]) {
+ // eslint-disable-next-line react-compiler/react-compiler
lineCountRef.current[activeFile] = code.split('\n').length;
}
const lineCount = lineCountRef.current[activeFile];
diff --git a/src/components/MDX/Sandpack/DownloadButton.tsx b/src/components/MDX/Sandpack/DownloadButton.tsx
index d6b1c3299..94cf13ddc 100644
--- a/src/components/MDX/Sandpack/DownloadButton.tsx
+++ b/src/components/MDX/Sandpack/DownloadButton.tsx
@@ -5,6 +5,7 @@
import {useSyncExternalStore} from 'react';
import {useSandpack} from '@codesandbox/sandpack-react/unstyled';
import {IconDownload} from '../../Icon/IconDownload';
+import {AppJSPath, StylesCSSPath, SUPPORTED_FILES} from './createFileMap';
export interface DownloadButtonProps {}
let supportsImportMap = false;
@@ -32,8 +33,6 @@ function useSupportsImportMap() {
return useSyncExternalStore(subscribe, getCurrentValue, getServerSnapshot);
}
-const SUPPORTED_FILES = ['/App.js', '/styles.css'];
-
export function DownloadButton({
providedFiles,
}: {
@@ -49,8 +48,8 @@ export function DownloadButton({
}
const downloadHTML = () => {
- const css = sandpack.files['/styles.css']?.code ?? '';
- const code = sandpack.files['/App.js']?.code ?? '';
+ const css = sandpack.files[StylesCSSPath]?.code ?? '';
+ const code = sandpack.files[AppJSPath]?.code ?? '';
const blob = new Blob([
`
diff --git a/src/components/MDX/Sandpack/LoadingOverlay.tsx b/src/components/MDX/Sandpack/LoadingOverlay.tsx
index cd3f38fca..de883629c 100644
--- a/src/components/MDX/Sandpack/LoadingOverlay.tsx
+++ b/src/components/MDX/Sandpack/LoadingOverlay.tsx
@@ -17,7 +17,7 @@ export const LoadingOverlay = ({
clientId: string;
dependenciesLoading: boolean;
forceLoading: boolean;
-} & React.HTMLAttributes
): JSX.Element | null => {
+} & React.HTMLAttributes): React.ReactNode | null => {
const loadingOverlayState = useLoadingOverlayState(
clientId,
dependenciesLoading,
@@ -64,6 +64,7 @@ export const LoadingOverlay = ({
transition: `opacity ${FADE_ANIMATION_DURATION}ms ease-out`,
}}>
+ {/* @ts-ignore: the OpenInCodeSandboxButton type from '@codesandbox/sandpack-react/unstyled' is incompatible with JSX in React 19 */}
diff --git a/src/components/MDX/Sandpack/NavigationBar.tsx b/src/components/MDX/Sandpack/NavigationBar.tsx
index 26ed5783d..bf2c3186c 100644
--- a/src/components/MDX/Sandpack/NavigationBar.tsx
+++ b/src/components/MDX/Sandpack/NavigationBar.tsx
@@ -115,7 +115,10 @@ export function NavigationBar({providedFiles}: {providedFiles: Array
}) {
return (
+ {/* If Prettier reformats this block, the two @ts-ignore directives will no longer be adjacent to the problematic lines, causing TypeScript errors */}
+ {/* prettier-ignore */}
+ {/* @ts-ignore: the Listbox type from '@headlessui/react' is incompatible with JSX in React 19 */}
@@ -129,8 +132,10 @@ export function NavigationBar({providedFiles}: {providedFiles: Array}) {
'w-[fit-content]',
showDropdown ? 'invisible' : ''
)}>
+ {/* @ts-ignore: the FileTabs type from '@codesandbox/sandpack-react/unstyled' is incompatible with JSX in React 19 */}
+ {/* @ts-ignore: the Listbox type from '@headlessui/react' is incompatible with JSX in React 19 */}
{({open}) => (
// If tabs don't fit, display the dropdown instead.
@@ -160,10 +165,10 @@ export function NavigationBar({providedFiles}: {providedFiles: Array}) {
- {isMultiFile && showDropdown && (
-
- {visibleFiles.map((filePath: string) => (
-
+ {/* @ts-ignore: the Listbox type from '@headlessui/react' is incompatible with JSX in React 19 */}
+ {isMultiFile && showDropdown && (
+ {/* @ts-ignore: the Listbox type from '@headlessui/react' is incompatible with JSX in React 19 */}
+ {visibleFiles.map((filePath: string) => (
{({active}) => (
{
setShowLoading(true);
diff --git a/src/components/MDX/Sandpack/SandpackRoot.tsx b/src/components/MDX/Sandpack/SandpackRoot.tsx
index df0c757f2..67f40d0b3 100644
--- a/src/components/MDX/Sandpack/SandpackRoot.tsx
+++ b/src/components/MDX/Sandpack/SandpackRoot.tsx
@@ -9,6 +9,7 @@ import {SandpackLogLevel} from '@codesandbox/sandpack-client';
import {CustomPreset} from './CustomPreset';
import {createFileMap} from './createFileMap';
import {CustomTheme} from './Themes';
+import {template} from './template';
type SandpackProps = {
children: React.ReactNode;
@@ -70,22 +71,31 @@ function SandpackRoot(props: SandpackProps) {
const codeSnippets = Children.toArray(children) as React.ReactElement[];
const files = createFileMap(codeSnippets);
- files['/styles.css'] = {
- code: [sandboxStyle, files['/styles.css']?.code ?? ''].join('\n\n'),
- hidden: !files['/styles.css']?.visible,
+ if ('/index.html' in files) {
+ throw new Error(
+ 'You cannot use `index.html` file in sandboxes. ' +
+ 'Only `public/index.html` is respected by Sandpack and CodeSandbox (where forks are created).'
+ );
+ }
+
+ files['/src/styles.css'] = {
+ code: [sandboxStyle, files['/src/styles.css']?.code ?? ''].join('\n\n'),
+ hidden: !files['/src/styles.css']?.visible,
};
return (
diff --git a/src/components/MDX/Sandpack/createFileMap.ts b/src/components/MDX/Sandpack/createFileMap.ts
index 6fc160c46..193b07be8 100644
--- a/src/components/MDX/Sandpack/createFileMap.ts
+++ b/src/components/MDX/Sandpack/createFileMap.ts
@@ -3,14 +3,28 @@
*/
import type {SandpackFile} from '@codesandbox/sandpack-react/unstyled';
+import type {PropsWithChildren, ReactElement, HTMLAttributes} from 'react';
+
+export const AppJSPath = `/src/App.js`;
+export const StylesCSSPath = `/src/styles.css`;
+export const SUPPORTED_FILES = [AppJSPath, StylesCSSPath];
export const createFileMap = (codeSnippets: any) => {
return codeSnippets.reduce(
(result: Record, codeSnippet: React.ReactElement) => {
- if ((codeSnippet.type as any).mdxName !== 'pre') {
+ if (
+ (codeSnippet.type as any).mdxName !== 'pre' &&
+ codeSnippet.type !== 'pre'
+ ) {
return result;
}
- const {props} = codeSnippet.props.children;
+ const {props} = (
+ codeSnippet.props as PropsWithChildren<{
+ children: ReactElement<
+ HTMLAttributes & {meta?: string}
+ >;
+ }>
+ ).children;
let filePath; // path in the folder structure
let fileHidden = false; // if the file is available as a tab
let fileActive = false; // if the file tab is shown by default
@@ -26,15 +40,16 @@ export const createFileMap = (codeSnippets: any) => {
}
} else {
if (props.className === 'language-js') {
- filePath = '/App.js';
+ filePath = AppJSPath;
} else if (props.className === 'language-css') {
- filePath = '/styles.css';
+ filePath = StylesCSSPath;
} else {
throw new Error(
`Code block is missing a filename: ${props.children}`
);
}
}
+
if (result[filePath]) {
throw new Error(
`File ${filePath} was defined multiple times. Each file snippet should have a unique path name`
diff --git a/src/components/MDX/Sandpack/index.tsx b/src/components/MDX/Sandpack/index.tsx
index 6873547a1..6755ba8de 100644
--- a/src/components/MDX/Sandpack/index.tsx
+++ b/src/components/MDX/Sandpack/index.tsx
@@ -3,7 +3,7 @@
*/
import {lazy, memo, Children, Suspense} from 'react';
-import {createFileMap} from './createFileMap';
+import {AppJSPath, createFileMap} from './createFileMap';
const SandpackRoot = lazy(() => import('./SandpackRoot'));
@@ -57,7 +57,7 @@ export default memo(function SandpackWrapper(props: any): any {
);
let activeCode;
if (!activeCodeSnippet.length) {
- activeCode = codeSnippet['/App.js'].code;
+ activeCode = codeSnippet[AppJSPath].code;
} else {
activeCode = codeSnippet[activeCodeSnippet[0]].code;
}
diff --git a/src/components/MDX/Sandpack/template.ts b/src/components/MDX/Sandpack/template.ts
new file mode 100644
index 000000000..42f02f6a6
--- /dev/null
+++ b/src/components/MDX/Sandpack/template.ts
@@ -0,0 +1,54 @@
+export const template = {
+ '/src/index.js': {
+ hidden: true,
+ code: `import React, { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import "./styles.css";
+
+import App from "./App";
+
+const root = createRoot(document.getElementById("root"));
+root.render(
+
+
+
+);`,
+ },
+ '/package.json': {
+ hidden: true,
+ code: JSON.stringify(
+ {
+ name: 'react.dev',
+ version: '0.0.0',
+ main: '/src/index.js',
+ scripts: {
+ start: 'react-scripts start',
+ build: 'react-scripts build',
+ test: 'react-scripts test --env=jsdom',
+ eject: 'react-scripts eject',
+ },
+ dependencies: {
+ react: '19.0.0-rc-3edc000d-20240926',
+ 'react-dom': '19.0.0-rc-3edc000d-20240926',
+ 'react-scripts': '^5.0.0',
+ },
+ },
+ null,
+ 2
+ ),
+ },
+ '/public/index.html': {
+ hidden: true,
+ code: `
+
+
+
+
+ Document
+
+
+
+
+`,
+ },
+};
diff --git a/src/components/MDX/SandpackWithHTMLOutput.tsx b/src/components/MDX/SandpackWithHTMLOutput.tsx
new file mode 100644
index 000000000..51ce28dc1
--- /dev/null
+++ b/src/components/MDX/SandpackWithHTMLOutput.tsx
@@ -0,0 +1,85 @@
+import {Children, memo} from 'react';
+import InlineCode from './InlineCode';
+import Sandpack from './Sandpack';
+
+const ShowRenderedHTML = `
+import { renderToStaticMarkup } from 'react-dom/server';
+import formatHTML from './formatHTML.js';
+
+export default function ShowRenderedHTML({children}) {
+ const markup = renderToStaticMarkup(
+
+
+ {children}
+
+ );
+ return (
+ <>
+ Rendered HTML:
+
+ {formatHTML(markup)}
+
+ >
+ );
+}`;
+
+const formatHTML = `
+import format from 'html-format';
+
+export default function formatHTML(markup) {
+ // Cheap tricks to format the HTML readably -- haven't been able to
+ // find a package that runs in browser and prettifies the HTML if it
+ // lacks line-breaks.
+ return format(markup
+ .replace('', '\\n')
+ .replace('', '\\n')
+ .replaceAll(/<\\/script>/g, '<\\/script>\\n')
+ .replaceAll(/`), require careful positioning in the DOM due to style precedence rules. Building a stylesheet capability that allows for composability within components is hard, so users often end up either loading all of their styles far from the components that may depend on them, or they use a style library which encapsulates this complexity.
+
+In React 19, we're addressing this complexity and providing even deeper integration into Concurrent Rendering on the Client and Streaming Rendering on the Server with built in support for stylesheets. If you tell React the `precedence` of your stylesheet it will manage the insertion order of the stylesheet in the DOM and ensure that the stylesheet (if external) is loaded before revealing content that depends on those style rules.
+
+```js {4,5,17}
+function ComponentOne() {
+ return (
+
+
+
+
+ {...}
+
+
+ )
+}
+
+function ComponentTwo() {
+ return (
+
+
{...}
+
<-- will be inserted between foo & bar
+
+ )
+}
+```
+
+During Server Side Rendering React will include the stylesheet in the ``, which ensures that the browser will not paint until it has loaded. If the stylesheet is discovered late after we've already started streaming, React will ensure that the stylesheet is inserted into the `` on the client before revealing the content of a Suspense boundary that depends on that stylesheet.
+
+During Client Side Rendering React will wait for newly rendered stylesheets to load before committing the render. If you render this component from multiple places within your application React will only include the stylesheet once in the document:
+
+```js {5}
+function App() {
+ return <>
+
+ ...
+ // won't lead to a duplicate stylesheet link in the DOM
+ >
+}
+```
+
+For users accustomed to loading stylesheets manually this is an opportunity to locate those stylesheets alongside the components that depend on them allowing for better local reasoning and an easier time ensuring you only load the stylesheets that you actually depend on.
+
+Style libraries and style integrations with bundlers can also adopt this new capability so even if you don't directly render your own stylesheets, you can still benefit as your tools are upgraded to use this feature.
+
+For more details, read the docs for [``](/reference/react-dom/components/link) and [`
+```
+
+
+
+
+
+---
+
+## Reference {/*reference*/}
+
+### `
+```
+
+[See more examples below.](#usage)
+
+#### Props {/*props*/}
+
+`
+
+ >
+ );
+}
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
diff --git a/src/content/reference/react-dom/components/textarea.md b/src/content/reference/react-dom/components/textarea.md
index 5c742f293..9bd29fa38 100644
--- a/src/content/reference/react-dom/components/textarea.md
+++ b/src/content/reference/react-dom/components/textarea.md
@@ -55,7 +55,7 @@ These `