@samitouri / QOS-React / commits / 39c2c1d450

[Float] Support `fetchPriority` for module resources (#36835)

Alexander Lichter committed Jun 24, 2026 at 19:41 UTC 39c2c1d45001021a99f507f83b946c6b08c8bbdf
4 files changed +263 -16
packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+167
@@ -6532,6 +6532,67 @@ body {
6532 );
6533 });
6534
6535 + it('supports fetchPriority', async () => {
6536 + function Component({isServer}) {
6537 + const suffix = isServer ? 'server' : 'client';
6538 + ReactDOM.preloadModule('high' + suffix, {
6539 + fetchPriority: 'high',
6540 + });
6541 + ReactDOM.preloadModule('low' + suffix, {
6542 + fetchPriority: 'low',
6543 + });
6544 + ReactDOM.preloadModule('auto' + suffix, {
6545 + fetchPriority: 'auto',
6546 + });
6547 + return 'hello';
6548 + }
6549 +
6550 + await act(() => {
6551 + renderToPipeableStream(
6552 + <html>
6553 + <body>
6554 + <Component isServer={true} />
6555 + </body>
6556 + </html>,
6557 + ).pipe(writable);
6558 + });
6559 +
6560 + expect(getMeaningfulChildren(document)).toEqual(
6561 + <html>
6562 + <head>
6563 + <link rel="modulepreload" href="highserver" fetchpriority="high" />
6564 + <link rel="modulepreload" href="lowserver" fetchpriority="low" />
6565 + <link rel="modulepreload" href="autoserver" fetchpriority="auto" />
6566 + </head>
6567 + <body>hello</body>
6568 + </html>,
6569 + );
6570 +
6571 + ReactDOMClient.hydrateRoot(
6572 + document,
6573 + <html>
6574 + <body>
6575 + <Component />
6576 + </body>
6577 + </html>,
6578 + );
6579 + await waitForAll([]);
6580 +
6581 + expect(getMeaningfulChildren(document)).toEqual(
6582 + <html>
6583 + <head>
6584 + <link rel="modulepreload" href="highserver" fetchpriority="high" />
6585 + <link rel="modulepreload" href="lowserver" fetchpriority="low" />
6586 + <link rel="modulepreload" href="autoserver" fetchpriority="auto" />
6587 + <link rel="modulepreload" href="highclient" fetchpriority="high" />
6588 + <link rel="modulepreload" href="lowclient" fetchpriority="low" />
6589 + <link rel="modulepreload" href="autoclient" fetchpriority="auto" />
6590 + </head>
6591 + <body>hello</body>
6592 + </html>,
6593 + );
6594 + });
6595 +
6596 it('preloads multiple non-script modules with the same as type', async () => {
6597 function App() {
6598 ReactDOM.preloadModule('serviceworker one', {as: 'serviceworker'});
@@ -7266,6 +7327,112 @@ body {
7327 );
7328 });
7329
7330 + it('supports fetchPriority', async () => {
7331 + function Component({isServer}) {
7332 + const suffix = isServer ? 'server' : 'client';
7333 + ReactDOM.preinitModule('high' + suffix, {
7334 + fetchPriority: 'high',
7335 + });
7336 + ReactDOM.preinitModule('low' + suffix, {
7337 + fetchPriority: 'low',
7338 + });
7339 + ReactDOM.preinitModule('auto' + suffix, {
7340 + fetchPriority: 'auto',
7341 + });
7342 + return 'hello';
7343 + }
7344 +
7345 + await act(() => {
7346 + renderToPipeableStream(
7347 + <html>
7348 + <body>
7349 + <Component isServer={true} />
7350 + </body>
7351 + </html>,
7352 + ).pipe(writable);
7353 + });
7354 +
7355 + expect(getMeaningfulChildren(document)).toEqual(
7356 + <html>
7357 + <head>
7358 + <script
7359 + type="module"
7360 + src="highserver"
7361 + fetchpriority="high"
7362 + async=""
7363 + />
7364 + <script
7365 + type="module"
7366 + src="lowserver"
7367 + fetchpriority="low"
7368 + async=""
7369 + />
7370 + <script
7371 + type="module"
7372 + src="autoserver"
7373 + fetchpriority="auto"
7374 + async=""
7375 + />
7376 + </head>
7377 + <body>hello</body>
7378 + </html>,
7379 + );
7380 +
7381 + ReactDOMClient.hydrateRoot(
7382 + document,
7383 + <html>
7384 + <body>
7385 + <Component />
7386 + </body>
7387 + </html>,
7388 + );
7389 + await waitForAll([]);
7390 +
7391 + expect(getMeaningfulChildren(document)).toEqual(
7392 + <html>
7393 + <head>
7394 + <script
7395 + type="module"
7396 + src="highserver"
7397 + fetchpriority="high"
7398 + async=""
7399 + />
7400 + <script
7401 + type="module"
7402 + src="lowserver"
7403 + fetchpriority="low"
7404 + async=""
7405 + />
7406 + <script
7407 + type="module"
7408 + src="autoserver"
7409 + fetchpriority="auto"
7410 + async=""
7411 + />
7412 + <script
7413 + type="module"
7414 + src="highclient"
7415 + fetchpriority="high"
7416 + async=""
7417 + />
7418 + <script
7419 + type="module"
7420 + src="lowclient"
7421 + fetchpriority="low"
7422 + async=""
7423 + />
7424 + <script
7425 + type="module"
7426 + src="autoclient"
7427 + fetchpriority="auto"
7428 + async=""
7429 + />
7430 + </head>
7431 + <body>hello</body>
7432 + </html>,
7433 + );
7434 + });
7435 +
7436 it('warns if you provide invalid arguments', async () => {
7437 function App() {
7438 ReactDOM.preinitModule();
packages/react-dom/src/shared/ReactDOMFloat.js
+8
@@ -192,6 +192,10 @@ export function preloadModule(href: string, options?: ?PreloadModuleOptions) {
192 typeof options.integrity === 'string'
193 ? options.integrity
194 : undefined,
195 + fetchPriority:
196 + typeof options.fetchPriority === 'string'
197 + ? options.fetchPriority
198 + : undefined,
199 });
200 } else {
201 ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */
@@ -321,6 +325,10 @@ export function preinitModule(href: string, options?: ?PreinitModuleOptions) {
325 : undefined,
326 nonce:
327 typeof options.nonce === 'string' ? options.nonce : undefined,
328 + fetchPriority:
329 + typeof options.fetchPriority === 'string'
330 + ? options.fetchPriority
331 + : undefined,
332 });
333 }
334 } else if (options == null) {
packages/react-dom/src/shared/ReactDOMTypes.js
+4
@@ -28,6 +28,7 @@ export type PreloadModuleOptions = {
28 crossOrigin?: string,
29 integrity?: string,
30 nonce?: string,
31 + fetchPriority?: FetchPriorityEnum,
32 };
33 export type PreinitOptions = {
34 as: string,
@@ -42,6 +43,7 @@ export type PreinitModuleOptions = {
43 crossOrigin?: string,
44 integrity?: string,
45 nonce?: string,
46 + fetchPriority?: FetchPriorityEnum,
47 };
48
49 export type CrossOriginEnum = '' | 'use-credentials' | CrossOriginString;
@@ -63,6 +65,7 @@ export type PreloadModuleImplOptions = {
65 crossOrigin?: ?CrossOriginEnum,
66 integrity?: ?string,
67 nonce?: ?string,
68 + fetchPriority?: ?string,
69 };
70 export type PreinitStyleOptions = {
71 crossOrigin?: ?CrossOriginEnum,
@@ -79,6 +82,7 @@ export type PreinitModuleScriptOptions = {
82 crossOrigin?: ?CrossOriginEnum,
83 integrity?: ?string,
84 nonce?: ?string,
85 + fetchPriority?: ?string,
86 };
87
88 export type HostDispatcher = {
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js
+84 -16
@@ -1704,20 +1704,32 @@ describe('ReactFlightDOM', () => {
1704 FlightReactDOM.preconnect('c2 before', {crossOrigin: 'anonymous'});
1705 FlightReactDOM.preload('l before', {as: 'style'});
1706 FlightReactDOM.preloadModule('lm before');
1707 - FlightReactDOM.preloadModule('lm2 before', {crossOrigin: 'anonymous'});
1707 + FlightReactDOM.preloadModule('lm2 before', {
1708 + crossOrigin: 'anonymous',
1709 + fetchPriority: 'low',
1710 + });
1711 FlightReactDOM.preinit('i before', {as: 'script'});
1712 FlightReactDOM.preinitModule('m before');
1710 - FlightReactDOM.preinitModule('m2 before', {crossOrigin: 'anonymous'});
1713 + FlightReactDOM.preinitModule('m2 before', {
1714 + crossOrigin: 'anonymous',
1715 + fetchPriority: 'high',
1716 + });
1717 await 1;
1718 FlightReactDOM.prefetchDNS('d after');
1719 FlightReactDOM.preconnect('c after');
1720 FlightReactDOM.preconnect('c2 after', {crossOrigin: 'anonymous'});
1721 FlightReactDOM.preload('l after', {as: 'style'});
1722 FlightReactDOM.preloadModule('lm after');
1717 - FlightReactDOM.preloadModule('lm2 after', {crossOrigin: 'anonymous'});
1723 + FlightReactDOM.preloadModule('lm2 after', {
1724 + crossOrigin: 'anonymous',
1725 + fetchPriority: 'low',
1726 + });
1727 FlightReactDOM.preinit('i after', {as: 'script'});
1728 FlightReactDOM.preinitModule('m after');
1720 - FlightReactDOM.preinitModule('m2 after', {crossOrigin: 'anonymous'});
1729 + FlightReactDOM.preinitModule('m2 after', {
1730 + crossOrigin: 'anonymous',
1731 + fetchPriority: 'high',
1732 + });
1733 return <ClientComponent />;
1734 }
1735
@@ -1766,19 +1778,41 @@ describe('ReactFlightDOM', () => {
1778 <link rel="preconnect" href="c2 before" crossorigin="" />
1779 <link rel="preload" as="style" href="l before" />
1780 <link rel="modulepreload" href="lm before" />
1769 - <link rel="modulepreload" href="lm2 before" crossorigin="" />
1781 + <link
1782 + rel="modulepreload"
1783 + href="lm2 before"
1784 + crossorigin=""
1785 + fetchpriority="low"
1786 + />
1787 <script async="" src="i before" />
1788 <script type="module" async="" src="m before" />
1772 - <script type="module" async="" src="m2 before" crossorigin="" />
1789 + <script
1790 + type="module"
1791 + async=""
1792 + src="m2 before"
1793 + crossorigin=""
1794 + fetchpriority="high"
1795 + />
1796 <link rel="dns-prefetch" href="d after" />
1797 <link rel="preconnect" href="c after" />
1798 <link rel="preconnect" href="c2 after" crossorigin="" />
1799 <link rel="preload" as="style" href="l after" />
1800 <link rel="modulepreload" href="lm after" />
1778 - <link rel="modulepreload" href="lm2 after" crossorigin="" />
1801 + <link
1802 + rel="modulepreload"
1803 + href="lm2 after"
1804 + crossorigin=""
1805 + fetchpriority="low"
1806 + />
1807 <script async="" src="i after" />
1808 <script type="module" async="" src="m after" />
1781 - <script type="module" async="" src="m2 after" crossorigin="" />
1809 + <script
1810 + type="module"
1811 + async=""
1812 + src="m2 after"
1813 + crossorigin=""
1814 + fetchpriority="high"
1815 + />
1816 </head>
1817 <body />
1818 </html>,
@@ -1799,20 +1833,32 @@ describe('ReactFlightDOM', () => {
1833 FlightReactDOM.preconnect('c2 before', {crossOrigin: 'anonymous'});
1834 FlightReactDOM.preload('l before', {as: 'style'});
1835 FlightReactDOM.preloadModule('lm before');
1802 - FlightReactDOM.preloadModule('lm2 before', {crossOrigin: 'anonymous'});
1836 + FlightReactDOM.preloadModule('lm2 before', {
1837 + crossOrigin: 'anonymous',
1838 + fetchPriority: 'low',
1839 + });
1840 FlightReactDOM.preinit('i before', {as: 'script'});
1841 FlightReactDOM.preinitModule('m before');
1805 - FlightReactDOM.preinitModule('m2 before', {crossOrigin: 'anonymous'});
1842 + FlightReactDOM.preinitModule('m2 before', {
1843 + crossOrigin: 'anonymous',
1844 + fetchPriority: 'high',
1845 + });
1846 await 1;
1847 FlightReactDOM.prefetchDNS('d after');
1848 FlightReactDOM.preconnect('c after');
1849 FlightReactDOM.preconnect('c2 after', {crossOrigin: 'anonymous'});
1850 FlightReactDOM.preload('l after', {as: 'style'});
1851 FlightReactDOM.preloadModule('lm after');
1812 - FlightReactDOM.preloadModule('lm2 after', {crossOrigin: 'anonymous'});
1852 + FlightReactDOM.preloadModule('lm2 after', {
1853 + crossOrigin: 'anonymous',
1854 + fetchPriority: 'low',
1855 + });
1856 FlightReactDOM.preinit('i after', {as: 'script'});
1857 FlightReactDOM.preinitModule('m after');
1815 - FlightReactDOM.preinitModule('m2 after', {crossOrigin: 'anonymous'});
1858 + FlightReactDOM.preinitModule('m2 after', {
1859 + crossOrigin: 'anonymous',
1860 + fetchPriority: 'high',
1861 + });
1862 return <ClientComponent />;
1863 }
1864
@@ -1866,16 +1912,38 @@ describe('ReactFlightDOM', () => {
1912 <link rel="preconnect" href="c2 after" crossorigin="" />
1913 <script async="" src="i before" />
1914 <script type="module" async="" src="m before" />
1869 - <script type="module" async="" src="m2 before" crossorigin="" />
1915 + <script
1916 + type="module"
1917 + async=""
1918 + src="m2 before"
1919 + crossorigin=""
1920 + fetchpriority="high"
1921 + />
1922 <script async="" src="i after" />
1923 <script type="module" async="" src="m after" />
1872 - <script type="module" async="" src="m2 after" crossorigin="" />
1924 + <script
1925 + type="module"
1926 + async=""
1927 + src="m2 after"
1928 + crossorigin=""
1929 + fetchpriority="high"
1930 + />
1931 <link rel="preload" as="style" href="l before" />
1932 <link rel="modulepreload" href="lm before" />
1875 - <link rel="modulepreload" href="lm2 before" crossorigin="" />
1933 + <link
1934 + rel="modulepreload"
1935 + href="lm2 before"
1936 + crossorigin=""
1937 + fetchpriority="low"
1938 + />
1939 <link rel="preload" as="style" href="l after" />
1940 <link rel="modulepreload" href="lm after" />
1878 - <link rel="modulepreload" href="lm2 after" crossorigin="" />
1941 + <link
1942 + rel="modulepreload"
1943 + href="lm2 after"
1944 + crossorigin=""
1945 + fetchpriority="low"
1946 + />
1947 </head>
1948 <body>
1949 <p>hello world</p>