diff --git a/astro.config.ts b/astro.config.ts
index a9944ea59a8..361e9934eae 100644
--- a/astro.config.ts
+++ b/astro.config.ts
@@ -43,6 +43,18 @@ const canonicalUrlsWithLanguageVariants = extractCanonicalUrlsWithLanguageVarian
export default defineConfig({
site: "https://docs.chain.link",
trailingSlash: "never",
+ // Prefetch internal links so client-side navigations (ClientRouter) feel instant.
+ // "hover" (not "viewport") avoids prefetching the hundreds of links in the docs sidebar at once.
+ //
+ // NOTE (Astro 5 -> 7 upgrade): `prefetch` and `` are stable in Astro 5 and remain
+ // the supported API in Astro 6/7, so this config carries over unchanged. The one thing to revisit
+ // is `patches/astro+5.18.2.patch` (dev-server module-graph perf fix): it is pinned to 5.18.2 and
+ // will fail to apply after the upgrade. Check whether the upstream fix has landed by then; if not,
+ // regenerate the patch against the new version with `npx patch-package astro`.
+ prefetch: {
+ prefetchAll: true,
+ defaultStrategy: "hover",
+ },
redirects: {
"/ccip/directory": "/ccip/directory/mainnet",
"/ccip/supported-networks": "/ccip/directory/mainnet",
diff --git a/patches/astro+5.18.2.patch b/patches/astro+5.18.2.patch
new file mode 100644
index 00000000000..2326c547f7f
--- /dev/null
+++ b/patches/astro+5.18.2.patch
@@ -0,0 +1,21 @@
+diff --git a/node_modules/astro/dist/vite-plugin-astro-server/vite.js b/node_modules/astro/dist/vite-plugin-astro-server/vite.js
+index 81bb5cf..e2f060c 100644
+--- a/node_modules/astro/dist/vite-plugin-astro-server/vite.js
++++ b/node_modules/astro/dist/vite-plugin-astro-server/vite.js
+@@ -60,6 +60,16 @@ async function* crawlGraph(loader, _id, isRootFile, scanned = /* @__PURE__ */ ne
+ }
+ }
+ function isImportedBy(parent, entry) {
++ // Vite 6's backward-compatible ModuleNode rebuilds a union Set (and looks up a compat
++ // node for every importer) each time `importers` is read. With ~3.7k icons importing
++ // one module that is ~27M iterations per request. Read the per-environment Sets directly.
++ const client = entry._clientModule;
++ const ssr = entry._ssrModule;
++ if (client || ssr) {
++ if (client) for (const importer of client.importers) if (importer.id === parent) return true;
++ if (ssr) for (const importer of ssr.importers) if (importer.id === parent) return true;
++ return false;
++ }
+ for (const importer of entry.importers) {
+ if (importer.id === parent) {
+ return true;
diff --git a/src/components/CRETemplate/CRETemplateOverview.astro b/src/components/CRETemplate/CRETemplateOverview.astro
index cd4f5a7db78..a408b5268c2 100644
--- a/src/components/CRETemplate/CRETemplateOverview.astro
+++ b/src/components/CRETemplate/CRETemplateOverview.astro
@@ -80,7 +80,7 @@ const { frontmatter } = Astro.props
-
diff --git a/src/components/NavigationProgress.astro b/src/components/NavigationProgress.astro
new file mode 100644
index 00000000000..fc86a5d67c9
--- /dev/null
+++ b/src/components/NavigationProgress.astro
@@ -0,0 +1,94 @@
+---
+/**
+ * Top-of-page progress bar for client-side navigation.
+ *
+ * With there is no browser loading indicator, so a slow navigation looks like a
+ * dead click. This restores that feedback: the bar appears when a navigation starts and completes
+ * when the new page is swapped in.
+ *
+ * `transition:persist` keeps the element across swaps, so the script's element references stay
+ * valid and the bar never restarts mid-navigation.
+ */
+---
+
+
+
+
+
+
diff --git a/src/components/PageTabs.astro b/src/components/PageTabs.astro
index 12ff4ad2696..3bb224b02d6 100644
--- a/src/components/PageTabs.astro
+++ b/src/components/PageTabs.astro
@@ -466,7 +466,7 @@ const displayPage = hasActivePage ? (Array.isArray(currentPageGroup) ? currentPa
})
}
- window.addEventListener("load", initDropdown)
+ document.addEventListener("astro:page-load", initDropdown)
// Update padding based on header visibility
const section = document.querySelector(".tabs-section") as HTMLElement
diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro
index 4974f73342e..e2094bdb58c 100644
--- a/src/layouts/BaseLayout.astro
+++ b/src/layouts/BaseLayout.astro
@@ -1,8 +1,10 @@
---
+import { ClientRouter } from "astro:transitions"
import HeadCommon from "~/components/HeadCommon.astro"
import HeadSEO from "~/components/HeadSEO.astro"
import StructuredData from "~/components/StructuredData.astro"
import Header from "~/components/Header/Header.astro"
+import NavigationProgress from "~/components/NavigationProgress.astro"
import { NewsletterCTA } from "~/components/Footer/NewsletterCTA"
import Footer from "~/components/Footer/Footer.astro"
import { Metadata, QuickstartsFrontmatter } from "~/content.config.ts"
@@ -27,6 +29,7 @@ const canonicalURL = new URL(canonicalURLStr)
+
-
+
+
+
diff --git a/src/pages/certification.astro b/src/pages/certification.astro
index dee853fb942..bc53e42e530 100644
--- a/src/pages/certification.astro
+++ b/src/pages/certification.astro
@@ -835,7 +835,7 @@ const formattedContentTitle = `Courses | Chainlink Certifications`