React SME Cookbook
All FAQs

Search Documentation

Search across all documentation pages

react-19preloadpreinitprefetchDNSpreconnectsuspenseperformance

Asset Loading - Preload fonts, scripts, and stylesheets with React DOM APIs

Recipe

import { prefetchDNS, preconnect, preload, preinit } from "react-dom";
 
function App() {
  // Eagerly load and execute a script
  preinit("https://cdn.example.com/analytics.js", { as: "script" });
 
  // Preload a font (download early, use later)
  preload("https://fonts.example.com/inter.woff2", {
    as: "font",
    type: "font/woff2",
    crossOrigin: "anonymous",
  });
 
  // Preload a stylesheet
  preload("https://cdn.example.com/theme.css", { as: "style" });
 
  // DNS prefetch for a domain you will request later
  prefetchDNS("https://api.example.com");
 
  // Preconnect (DNS + TCP + TLS) for a domain you will request soon
  preconnect("https://images.example.com");
 
  return <main>{/* ... */}</main>;
}

When to reach for this: Use these APIs to improve initial page load and client-side navigation performance by telling the browser about resources it will need before React renders the components that use them.

Working Example

// A page that preloads all critical resources for optimal performance
import { Suspense } from "react";
import { preload, preinit, preconnect } from "react-dom";
 
// Preload critical resources at the module level (runs once on import)
preconnect("https://api.myapp.com");
preload("/fonts/brand-bold.woff2", { as: "font", type: "font/woff2", crossOrigin: "anonymous" });
preload("/fonts/brand-regular.woff2", { as: "font", type: "font/woff2", crossOrigin: "anonymous" });
 
function CriticalCSS() {
  return (
    <>
      {/* React deduplicates and manages stylesheet loading order via precedence */}
      <link rel="stylesheet" href="/styles/reset.css" precedence="reset" />
      <link rel="stylesheet" href="/styles/base.css" precedence="default" />
      <link rel="stylesheet" href="/styles/theme.css" precedence="high" />
    </>
  );
}
 
function HeavyWidget() {
  // Preinit a script needed by this widget
  preinit("/scripts/chart-library.js", { as: "script" });
 
  return (
    <div className="widget">
      <h2>Sales Dashboard</h2>
      <div id="chart-container" />
    </div>
  );
}
 
function ImageGallery({ images }: { images: { src: string; alt: string }[] }) {
  // Preload the first few images for instant display
  images.slice(0, 3).forEach((img) => {
    preload(img.src, { as: "image" });
  });
 
  return (
    <div className="grid grid-cols-3 gap-4">
      {images.map((img, i) => (
        <img
          key={i}
          src={img.src}
          alt={img.alt}
          loading={i < 3 ? "eager" : "lazy"}
        />
      ))}
    </div>
  );
}
 
export default function DashboardPage() {
  return (
    <>
      <CriticalCSS />
      <title>Dashboard | MyApp</title>
 
      <header>
        <h1>Dashboard</h1>
      </header>
 
      <Suspense fallback={<p>Loading widget...</p>}>
        <HeavyWidget />
      </Suspense>
 
      <Suspense fallback={<p>Loading gallery...</p>}>
        <ImageGallery
          images={[
            { src: "/images/product-1.jpg", alt: "Product 1" },
            { src: "/images/product-2.jpg", alt: "Product 2" },
            { src: "/images/product-3.jpg", alt: "Product 3" },
            { src: "/images/product-4.jpg", alt: "Product 4" },
            { src: "/images/product-5.jpg", alt: "Product 5" },
            { src: "/images/product-6.jpg", alt: "Product 6" },
          ]}
        />
      </Suspense>
    </>
  );
}

What this demonstrates:

  • preconnect establishing early connections to API domains
  • preload for fonts at the module level (before any component renders)
  • <link rel="stylesheet" precedence={}> for ordered, deduplicated CSS
  • preinit eagerly loading a third-party script inside a component
  • preload for images to speed up above-the-fold rendering
  • Suspense boundaries around heavy components for streaming

Deep Dive

How It Works

  • preload(href, options) -- Inserts a <link rel="preload"> tag into the document. The browser downloads the resource at high priority but does not execute or apply it. Use for fonts, images, stylesheets, and scripts you will need soon.
  • preinit(href, options) -- Downloads and executes/applies the resource immediately. For scripts, this means the script runs. For stylesheets, this means the CSS is applied. Use when you need the resource active right away.
  • prefetchDNS(href) -- Inserts a <link rel="dns-prefetch"> hint. The browser resolves the domain name early. Cheapest option -- use when you might request from this domain.
  • preconnect(href) -- Inserts a <link rel="preconnect">. The browser performs DNS + TCP + TLS handshake early. Use when you will definitely request from this domain soon.
  • All four APIs can be called anywhere -- inside components, event handlers, module scope, or even effects. React deduplicates calls with the same href.
  • During SSR, these calls emit the appropriate <link> tags in the HTML <head> and as early hints (103 Early Hints) if the server supports it.
  • Stylesheet Suspense -- <link rel="stylesheet" precedence={}> tells React to manage the stylesheet. React will suspend rendering of the component until the stylesheet loads, preventing a flash of unstyled content.

Variations

Preloading on hover (predictive):

"use client";
 
import { preload } from "react-dom";
import Link from "next/link";
 
function NavLink({ href, children }: { href: string; children: React.ReactNode }) {
  function handleMouseEnter() {
    // Preload the page's CSS and hero image when user hovers
    preload(`/styles${href}.css`, { as: "style" });
    preload(`/images${href}-hero.jpg`, { as: "image" });
  }
 
  return (
    <Link href={href} onMouseEnter={handleMouseEnter}>
      {children}
    </Link>
  );
}

Conditional preloading based on viewport:

"use client";
 
import { useEffect } from "react";
import { preload } from "react-dom";
 
function AdaptiveLoader() {
  useEffect(() => {
    const connection = (navigator as any).connection;
    if (connection?.effectiveType === "4g") {
      // Only preload heavy resources on fast connections
      preload("/videos/hero.mp4", { as: "video" });
      preload("/fonts/display.woff2", { as: "font", crossOrigin: "anonymous" });
    }
  }, []);
 
  return null;
}

Module-level preloading (runs on import):

import { preconnect, preload } from "react-dom";
 
// These execute when the module is imported, before any component renders
preconnect("https://cdn.example.com");
preload("https://cdn.example.com/critical.css", { as: "style" });
 
export default function Page() {
  return <div>Content that needs cdn.example.com resources</div>;
}

TypeScript Notes

  • All four functions are imported from "react-dom".
  • preload(href: string, options: { as: string; crossOrigin?: string; type?: string; fetchPriority?: "high" | "low" | "auto" }).
  • preinit(href: string, options: { as: "script" | "style"; crossOrigin?: string; fetchPriority?: "high" | "low" | "auto"; nonce?: string }).
  • prefetchDNS(href: string) and preconnect(href: string, options?: { crossOrigin?: string }) have minimal options.
  • The precedence attribute on <link rel="stylesheet"> is typed as string.

Gotchas

  • Preloading too many resources -- Browsers have limits on concurrent connections. Preloading everything is counterproductive. Fix: Only preload resources that are critical for the above-the-fold experience.
  • preinit executes scripts immediately -- If the script has side effects or depends on DOM elements that have not rendered yet, it may fail. Fix: Use preload instead and add a <script> tag when the DOM is ready, or ensure the script handles missing DOM gracefully.
  • Font preload without crossOrigin -- Fonts loaded via preload must specify crossOrigin: "anonymous" even for same-origin fonts, per the Fetch spec. Fix: Always include crossOrigin: "anonymous" for font preloads.
  • Stylesheet without precedence is not managed -- If you omit precedence from a <link rel="stylesheet">, React does not deduplicate or suspend for it. Fix: Add precedence to all stylesheet links you want React to manage.
  • SSR vs client preload timing -- During SSR, preload hints appear in the initial HTML. During client navigation, they are injected dynamically. The timing difference may affect performance characteristics. Fix: Test both SSR and client navigation paths.

Alternatives

ApproachWhen to choose
React DOM preload APIsReact 19 built-in, works with SSR and client rendering
HTML <link> in <head> templateStatic resources known at build time
Next.js <Script> componentNext.js projects needing script loading strategies
@next/font / next/fontNext.js font optimization with automatic subsetting
Manual document.createElement("link")Rare cases needing programmatic control outside React
Resource hints via HTTP headersServer-level optimization (103 Early Hints)

FAQs

What is the difference between preload and preinit?
  • preload downloads the resource at high priority but does not execute or apply it
  • preinit downloads and executes/applies the resource immediately (scripts run, CSS is applied)
  • Use preload for resources you will need soon; use preinit when you need them active right away
What is the difference between prefetchDNS and preconnect?
  • prefetchDNS only resolves the domain name (DNS lookup) -- cheapest option
  • preconnect performs DNS + TCP + TLS handshake -- more expensive but saves more time
  • Use prefetchDNS when you might request from a domain; use preconnect when you definitely will soon
Where can these asset loading APIs be called?
  • Anywhere: inside components, event handlers, module scope, or effects
  • React deduplicates calls with the same href
  • Module-level calls execute when the module is imported, before any component renders
How do you preload fonts correctly?
preload("/fonts/brand.woff2", {
  as: "font",
  type: "font/woff2",
  crossOrigin: "anonymous", // required for all fonts
});

You must include crossOrigin: "anonymous" even for same-origin fonts, per the Fetch spec.

How do you preload resources on hover for faster navigation?
function NavLink({ href, children }) {
  function handleMouseEnter() {
    preload(`/styles${href}.css`, { as: "style" });
    preload(`/images${href}-hero.jpg`, { as: "image" });
  }
  return (
    <a href={href} onMouseEnter={handleMouseEnter}>
      {children}
    </a>
  );
}
How does stylesheet Suspense work with the precedence attribute?
  • <link rel="stylesheet" precedence="default"> tells React to manage the stylesheet
  • React suspends rendering of the component until the stylesheet loads, preventing a flash of unstyled content
  • Stylesheets with the same precedence value are grouped together in <head>
How do these APIs behave during SSR vs client-side navigation?
  • During SSR, calls emit <link> tags in the HTML <head> and as 103 Early Hints if the server supports it
  • During client navigation, they are injected dynamically into the DOM
  • Test both SSR and client navigation paths as timing differences may affect performance
Can you conditionally preload based on network speed?
useEffect(() => {
  const conn = (navigator as any).connection;
  if (conn?.effectiveType === "4g") {
    preload("/videos/hero.mp4", { as: "video" });
    preload("/fonts/display.woff2", {
      as: "font",
      crossOrigin: "anonymous",
    });
  }
}, []);
Gotcha: Why is preloading too many resources counterproductive?
  • Browsers have limits on concurrent connections per domain
  • Preloading everything competes for bandwidth and can actually slow down critical resources
  • Only preload resources that are critical for the above-the-fold experience
Gotcha: My preinit script fails because the DOM is not ready. What should I do?
  • preinit executes scripts immediately, which may fail if the script depends on DOM elements that have not rendered
  • Use preload instead and add a <script> tag when the DOM is ready
  • Or ensure the script handles missing DOM elements gracefully
How are the asset loading APIs typed in TypeScript?
preload(href: string, options: {
  as: string;
  crossOrigin?: string;
  type?: string;
  fetchPriority?: "high" | "low" | "auto";
}): void;
 
preinit(href: string, options: {
  as: "script" | "style";
  crossOrigin?: string;
  fetchPriority?: "high" | "low" | "auto";
  nonce?: string;
}): void;

All four functions are imported from "react-dom".

What is the TypeScript type for the precedence attribute on stylesheet links?
  • The precedence attribute is typed as string on <link rel="stylesheet">
  • It is a React-specific extension not present in standard HTML typings
  • Any string value works; common choices are "reset", "default", and "high"
  • Metadata -- Rendering title, meta, and link tags in components
  • Server Components -- Preloading resources during server rendering
  • Overview -- Full list of React 19 features