diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml
index 07271cef6..87dcfdc73 100644
--- a/.github/workflows/analyze.yml
+++ b/.github/workflows/analyze.yml
@@ -16,7 +16,7 @@ jobs:
- name: Set up node
uses: actions/setup-node@v1
with:
- node-version: "14.x"
+ node-version: '20.x'
- name: Install dependencies
uses: bahmutov/npm-install@v1.7.10
diff --git a/.github/workflows/site_lint.yml b/.github/workflows/site_lint.yml
index bf446393a..560e22643 100644
--- a/.github/workflows/site_lint.yml
+++ b/.github/workflows/site_lint.yml
@@ -11,14 +11,14 @@ jobs:
lint:
runs-on: ubuntu-latest
- name: Lint on node 12.x and ubuntu-latest
+ name: Lint on node 20.x and ubuntu-latest
steps:
- uses: actions/checkout@v1
- - name: Use Node.js 12.x
+ - name: Use Node.js 20.x
uses: actions/setup-node@v1
with:
- node-version: 12.x
+ node-version: 20.x
- name: Install deps and build (with cache)
uses: bahmutov/npm-install@v1.7.10
diff --git a/next.config.js b/next.config.js
index 2ea3e916e..414728580 100644
--- a/next.config.js
+++ b/next.config.js
@@ -9,10 +9,10 @@ const nextConfig = {
pageExtensions: ['jsx', 'js', 'ts', 'tsx', 'mdx', 'md'],
reactStrictMode: true,
experimental: {
- plugins: true,
+ // TODO: Remove after https://github.com/vercel/next.js/issues/49355 is fixed
+ appDir: false,
scrollRestoration: true,
legacyBrowsers: false,
- browsersListForSwc: true,
},
env: {
SANDPACK_BARE_COMPONENTS: process.env.SANDPACK_BARE_COMPONENTS,
diff --git a/package.json b/package.json
index f4f9a8026..e12f2aa46 100644
--- a/package.json
+++ b/package.json
@@ -32,12 +32,12 @@
"debounce": "^1.2.1",
"ga-lite": "^2.1.4",
"github-slugger": "^1.3.0",
- "next": "12.3.2-canary.7",
+ "next": "^13.4.1",
"next-remote-watch": "^1.0.0",
"parse-numeric-range": "^1.2.0",
- "react": "0.0.0-experimental-cb5084d1c-20220924",
+ "react": "^0.0.0-experimental-16d053d59-20230506",
"react-collapsed": "npm:@gaearon/react-collapsed@3.1.0-forked.1",
- "react-dom": "0.0.0-experimental-cb5084d1c-20220924",
+ "react-dom": "^0.0.0-experimental-16d053d59-20230506",
"remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1"
},
@@ -98,7 +98,7 @@
"webpack-bundle-analyzer": "^4.5.0"
},
"engines": {
- "node": ">=12.x"
+ "node": "^16.8.0 || ^18.0.0 || ^19.0.0 || ^20.0.0"
},
"nextBundleAnalysis": {
"budget": null,
diff --git a/patches/next+12.3.2-canary.7.patch b/patches/next+12.3.2-canary.7.patch
deleted file mode 100644
index ee8d132de..000000000
--- a/patches/next+12.3.2-canary.7.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-diff --git a/node_modules/next/dist/server/render.js b/node_modules/next/dist/server/render.js
-index 3a141de..72a8749 100644
---- a/node_modules/next/dist/server/render.js
-+++ b/node_modules/next/dist/server/render.js
-@@ -752,9 +752,14 @@ async function renderToHTML(req, res, pathname, query, renderOpts) {
- // Enabling react concurrent rendering mode: __NEXT_REACT_ROOT = true
- const renderShell = async (EnhancedApp, EnhancedComponent)=>{
- const content = renderContent(EnhancedApp, EnhancedComponent);
-- return await (0, _nodeWebStreamsHelper).renderToInitialStream({
-- ReactDOMServer,
-- element: content
-+ return new Promise((resolve, reject) => {
-+ (0, _nodeWebStreamsHelper).renderToInitialStream({
-+ ReactDOMServer,
-+ element: content,
-+ streamOptions: {
-+ onError: reject
-+ }
-+ }).then(resolve, reject);
- });
- };
- const createBodyResult = (initialStream, suffix)=>{
diff --git a/patches/next+13.4.1.patch b/patches/next+13.4.1.patch
new file mode 100644
index 000000000..6de490aa4
--- /dev/null
+++ b/patches/next+13.4.1.patch
@@ -0,0 +1,22 @@
+diff --git a/node_modules/next/dist/server/render.js b/node_modules/next/dist/server/render.js
+index a1f8648..1b3d608 100644
+--- a/node_modules/next/dist/server/render.js
++++ b/node_modules/next/dist/server/render.js
+@@ -758,9 +758,14 @@ async function renderToHTML(req, res, pathname, query, renderOpts) {
+ // Always using react concurrent rendering mode with required react version 18.x
+ const renderShell = async (EnhancedApp, EnhancedComponent)=>{
+ const content = renderContent(EnhancedApp, EnhancedComponent);
+- return await (0, _nodewebstreamshelper.renderToInitialStream)({
+- ReactDOMServer: _serverbrowser.default,
+- element: content
++ return new Promise((resolve, reject) => {
++ (0, _nodewebstreamshelper.renderToInitialStream)({
++ ReactDOMServer: _serverbrowser.default,
++ element: content,
++ streamOptions: {
++ onError: reject
++ }
++ }).then(resolve, reject);
+ });
+ };
+ const createBodyResult = (0, _tracer.getTracer)().wrap(_constants2.RenderSpan.createBodyResult, (initialStream, suffix)=>{
diff --git a/src/components/Breadcrumbs.tsx b/src/components/Breadcrumbs.tsx
index ca3afa851..8eead2302 100644
--- a/src/components/Breadcrumbs.tsx
+++ b/src/components/Breadcrumbs.tsx
@@ -15,10 +15,10 @@ function Breadcrumbs({breadcrumbs}: {breadcrumbs: RouteItem[]}) {
!crumb.skipBreadcrumb && (
-
-
- {crumb.title}
-
+
+ {crumb.title}
& ButtonLinkProps) {
const classes = cn(
className,
'active:scale-[.98] transition-transform inline-flex font-bold items-center outline-none focus:outline-none focus-visible:outline focus-visible:outline-link focus:outline-offset-2 focus-visible:dark:focus:outline-link-dark leading-snug',
@@ -34,10 +34,13 @@ function ButtonLink({
}
);
return (
-
-
- {children}
-
+
+ {children}
);
}
diff --git a/src/components/DocsFooter.tsx b/src/components/DocsFooter.tsx
index d2c2c25de..a5c7324e2 100644
--- a/src/components/DocsFooter.tsx
+++ b/src/components/DocsFooter.tsx
@@ -66,25 +66,24 @@ function FooterLink({
type: 'Previous' | 'Next';
}) {
return (
-
-
-
-
-
- {type}
-
- {title}
+
+
+
+
+ {type}
-
+ {title}
+
);
}
diff --git a/src/components/Layout/Footer.tsx b/src/components/Layout/Footer.tsx
index 677899eec..4d0b06646 100644
--- a/src/components/Layout/Footer.tsx
+++ b/src/components/Layout/Footer.tsx
@@ -388,8 +388,8 @@ function FooterLink({
return (
);
diff --git a/src/components/Layout/HomeContent.js b/src/components/Layout/HomeContent.js
index 5ff366d31..ba3ff6ef1 100644
--- a/src/components/Layout/HomeContent.js
+++ b/src/components/Layout/HomeContent.js
@@ -1186,13 +1186,14 @@ async function Talks({ confId }) {
function useNestedScrollLock(ref) {
useEffect(() => {
+ let node = ref.current;
let isLocked = false;
let lastScroll = performance.now();
function handleScroll() {
if (!isLocked) {
isLocked = true;
- ref.current.style.pointerEvents = 'none';
+ node.style.pointerEvents = 'none';
}
lastScroll = performance.now();
}
@@ -1200,7 +1201,7 @@ function useNestedScrollLock(ref) {
function updateLock() {
if (isLocked && performance.now() - lastScroll > 150) {
isLocked = false;
- ref.current.style.pointerEvents = '';
+ node.style.pointerEvents = '';
}
}
diff --git a/src/components/Layout/Sidebar/SidebarLink.tsx b/src/components/Layout/Sidebar/SidebarLink.tsx
index 6889a4b10..050bf7c71 100644
--- a/src/components/Layout/Sidebar/SidebarLink.tsx
+++ b/src/components/Layout/Sidebar/SidebarLink.tsx
@@ -49,44 +49,43 @@ export function SidebarLink({
target = '_blank';
}
return (
-
- 0,
- 'pl-5': level < 2,
- 'text-base font-bold': level === 0,
- 'text-primary dark:text-primary-dark': level === 0 && !selected,
- 'text-base text-secondary dark:text-secondary-dark':
- level > 0 && !selected,
- 'text-base text-link dark:text-link-dark bg-highlight dark:bg-highlight-dark border-blue-40 hover:bg-highlight hover:text-link dark:hover:bg-highlight-dark dark:hover:text-link-dark':
- selected,
- 'dark:bg-gray-70 bg-gray-3 dark:hover:bg-gray-70 hover:bg-gray-3':
- isPending,
- }
- )}>
- {/* This here needs to be refactored ofc */}
+ 0,
+ 'pl-5': level < 2,
+ 'text-base font-bold': level === 0,
+ 'text-primary dark:text-primary-dark': level === 0 && !selected,
+ 'text-base text-secondary dark:text-secondary-dark':
+ level > 0 && !selected,
+ 'text-base text-link dark:text-link-dark bg-highlight dark:bg-highlight-dark border-blue-40 hover:bg-highlight hover:text-link dark:hover:bg-highlight-dark dark:hover:text-link-dark':
+ selected,
+ 'dark:bg-gray-70 bg-gray-3 dark:hover:bg-gray-70 hover:bg-gray-3':
+ isPending,
+ }
+ )}>
+ {/* This here needs to be refactored ofc */}
+
+ {title}
+
+ {isExpanded != null && !hideArrow && (
- {title}
+
- {isExpanded != null && !hideArrow && (
-
-
-
- )}
-
+ )}
);
}
diff --git a/src/components/Layout/TopNav/TopNav.tsx b/src/components/Layout/TopNav/TopNav.tsx
index 23860a16f..0157f1ba5 100644
--- a/src/components/Layout/TopNav/TopNav.tsx
+++ b/src/components/Layout/TopNav/TopNav.tsx
@@ -90,15 +90,17 @@ const githubIcon = (
);
-function Link({href, children, ...props}: JSX.IntrinsicElements['a']) {
+function Link({
+ href,
+ children,
+ ...props
+}: React.AnchorHTMLAttributes) {
return (
-
- {/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
-
- {children}
-
+
+ {children}
);
}
@@ -247,16 +249,15 @@ export default function TopNav({
{isOpen ? : }
diff --git a/src/components/MDX/BlogCard.tsx b/src/components/MDX/BlogCard.tsx
index 9d86f9211..ba610b111 100644
--- a/src/components/MDX/BlogCard.tsx
+++ b/src/components/MDX/BlogCard.tsx
@@ -16,62 +16,62 @@ export interface BlogCardProps {
function BlogCard({title, badge, date, icon, url, children}: BlogCardProps) {
return (
-
-
-
-
-
- {title}
-
-
-
-
- {icon === 'labs' && (
-
-
-
- )}
- {icon === 'blog' && (
-
-
-
- )}
- {date}
- {badge ? (
-
- New
-
- ) : null}
-
-
- {children}
-
- {children != null && (
-
- Read more
-
+
+
+
+
+ {title}
+
+
+
+
+ {icon === 'labs' && (
+
+
+
)}
+ {icon === 'blog' && (
+
+
+
+ )}
+ {date}
+ {badge ? (
+
+ New
+
+ ) : null}
+
+ {children}
+
+ {children != null && (
+
+ Read more
+
+ )}
-
+
);
}
diff --git a/src/components/MDX/Link.tsx b/src/components/MDX/Link.tsx
index 8986d07a5..7bf041e56 100644
--- a/src/components/MDX/Link.tsx
+++ b/src/components/MDX/Link.tsx
@@ -13,7 +13,7 @@ function Link({
className,
children,
...props
-}: JSX.IntrinsicElements['a']) {
+}: React.AnchorHTMLAttributes
) {
const classes =
'inline text-link dark:text-link-dark border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal';
const modifiedChildren = Children.toArray(children).map((child: any) => {
@@ -41,11 +41,8 @@ function Link({
{modifiedChildren}
) : (
-
- {/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
-
- {modifiedChildren}
-
+
+ {modifiedChildren}
)}
>
diff --git a/src/components/MDX/Sandpack/DownloadButton.tsx b/src/components/MDX/Sandpack/DownloadButton.tsx
index 4181dbe95..4d206fff8 100644
--- a/src/components/MDX/Sandpack/DownloadButton.tsx
+++ b/src/components/MDX/Sandpack/DownloadButton.tsx
@@ -7,19 +7,22 @@ import {useSandpack} from '@codesandbox/sandpack-react';
import {IconDownload} from '../../Icon/IconDownload';
export interface DownloadButtonProps {}
-let supportsImportMap: boolean | void;
+let supportsImportMap = false;
+
+function subscribe(cb: () => void) {
+ // This shouldn't actually need to update, but this works around
+ // https://github.com/facebook/react/issues/26095
+ let timeout = setTimeout(() => {
+ supportsImportMap =
+ (HTMLScriptElement as any).supports &&
+ (HTMLScriptElement as any).supports('importmap');
+ cb();
+ }, 0);
+ return () => clearTimeout(timeout);
+}
function useSupportsImportMap() {
- function subscribe() {
- // It never updates.
- return () => {};
- }
function getCurrentValue() {
- if (supportsImportMap === undefined) {
- supportsImportMap =
- (HTMLScriptElement as any).supports &&
- (HTMLScriptElement as any).supports('importmap');
- }
return supportsImportMap;
}
function getServerSnapshot() {
diff --git a/src/components/Search.tsx b/src/components/Search.tsx
index 0e8f84f0d..2a9743ec3 100644
--- a/src/components/Search.tsx
+++ b/src/components/Search.tsx
@@ -22,11 +22,7 @@ export interface SearchProps {
}
function Hit({hit, children}: any) {
- return (
-
- {children}
-
- );
+ return {children};
}
// Copy-pasted from @docsearch/react to avoid importing the whole bundle.
diff --git a/src/content/learn/manipulating-the-dom-with-refs.md b/src/content/learn/manipulating-the-dom-with-refs.md
index b5c193763..3e91a7694 100644
--- a/src/content/learn/manipulating-the-dom-with-refs.md
+++ b/src/content/learn/manipulating-the-dom-with-refs.md
@@ -31,7 +31,7 @@ Then, use it to declare a ref inside your component:
const myRef = useRef(null);
```
-Finally, pass it to the DOM node as the `ref` attribute:
+Finally, pass your ref as the `ref` attribute to the JSX tag for which you want to get the DOM node:
```js
diff --git a/src/content/learn/responding-to-events.md b/src/content/learn/responding-to-events.md
index 782b6c0f1..4450c4613 100644
--- a/src/content/learn/responding-to-events.md
+++ b/src/content/learn/responding-to-events.md
@@ -313,6 +313,12 @@ button { margin-right: 10px; }
Notice how the `App` component does not need to know *what* `Toolbar` will do with `onPlayMovie` or `onUploadImage`. That's an implementation detail of the `Toolbar`. Here, `Toolbar` passes them down as `onClick` handlers to its `Button`s, but it could later also trigger them on a keyboard shortcut. Naming props after app-specific interactions like `onPlayMovie` gives you the flexibility to change how they're used later.
+
+
+
+Make sure that you use the appropriate HTML tags for your event handlers. For example, to handle clicks, use [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) instead of ``. Using a real browser `
` enables built-in browser behaviors like keyboard navigation. If you don't like the default browser styling of a button and want to make it look more like a link or a different UI element, you can achieve it with CSS. [Learn more about writing accessible markup.](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML)
+
+
## Event propagation {/*event-propagation*/}
diff --git a/src/content/reference/react-dom/components/common.md b/src/content/reference/react-dom/components/common.md
index ca16b6a6e..f1e4fcd0c 100644
--- a/src/content/reference/react-dom/components/common.md
+++ b/src/content/reference/react-dom/components/common.md
@@ -156,7 +156,7 @@ nt.
* [`title`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title): A string. Specifies the tooltip text for the element.
* [`translate`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/translate): Either `'yes'` or `'no'`. Passing `'no'` excludes the element content from being translated.
-You can also pass custom attributes as props, for example `mycustomprop="someValue".` This can be useful when integrating with third-party libraries. The custom attribute name must be lowercase and must not start with `on`. The value will be converted to a string. If you pass `null` or `undefined`, the custom attribute will be removed.
+You can also pass custom attributes as props, for example `mycustomprop="someValue"`. This can be useful when integrating with third-party libraries. The custom attribute name must be lowercase and must not start with `on`. The value will be converted to a string. If you pass `null` or `undefined`, the custom attribute will be removed.
These events fire only for the [`