diff --git a/frontends/api/src/ssr/useMounted.ts b/frontends/api/src/ssr/useMounted.ts
new file mode 100644
index 0000000000..809b8d412f
--- /dev/null
+++ b/frontends/api/src/ssr/useMounted.ts
@@ -0,0 +1,24 @@
+import { useEffect, useState } from "react"
+
+/*
+ * Intended for cases where the client content would otherwise be different
+ * from the server content on the first render pass in the browser and therefore
+ * cause a hydration mismatch error. We've seen this for example when lazy loading
+ * components with next/dynamic, whuch produces a race condition with client only /
+ * session based API responses.
+ *
+ * https://react.dev/reference/react-dom/client/hydrateRoot#handling-different-client-and-server-content
+ */
+export const useMounted = () => {
+  const [mounted, setMounted] = useState(false)
+
+  useEffect(() => {
+    setMounted(true)
+
+    return () => {
+      setMounted(false)
+    }
+  }, [])
+
+  return mounted
+}
diff --git a/frontends/api/src/ssr/usePrefetchWarnings.test.ts b/frontends/api/src/ssr/usePrefetchWarnings.test.ts
index 1b937459e1..1f92c99e29 100644
--- a/frontends/api/src/ssr/usePrefetchWarnings.test.ts
+++ b/frontends/api/src/ssr/usePrefetchWarnings.test.ts
@@ -1,4 +1,4 @@
-import { renderHook } from "@testing-library/react"
+import { renderHook, waitFor } from "@testing-library/react"
 import { useQuery } from "@tanstack/react-query"
 import { usePrefetchWarnings } from "./usePrefetchWarnings"
 import { setupReactQueryTest } from "../hooks/test-utils"
@@ -35,6 +35,7 @@ describe("SSR prefetch warnings", () => {
       initialProps: { queryClient },
     })
 
+    await waitFor(() => expect(console.info).toHaveBeenCalledTimes(1))
     expect(console.info).toHaveBeenCalledWith(
       "The following queries were requested in first render but not prefetched.",
       "If these queries are user-specific, they cannot be prefetched - responses are cached on public CDN.",
@@ -97,6 +98,7 @@ describe("SSR prefetch warnings", () => {
       initialProps: { queryClient },
     })
 
+    await waitFor(() => expect(console.info).toHaveBeenCalledTimes(1))
     expect(console.info).toHaveBeenCalledWith(
       "The following queries were prefetched on the server but not accessed during initial render.",
       "If these queries are no longer in use they should removed from prefetch:",
diff --git a/frontends/api/src/ssr/usePrefetchWarnings.ts b/frontends/api/src/ssr/usePrefetchWarnings.ts
index 000d80261c..f198e282b2 100644
--- a/frontends/api/src/ssr/usePrefetchWarnings.ts
+++ b/frontends/api/src/ssr/usePrefetchWarnings.ts
@@ -1,5 +1,6 @@
-import { useEffect } from "react"
+import { useEffect, useState } from "react"
 import type { Query, QueryClient, QueryKey } from "@tanstack/react-query"
+import { useMounted } from "./useMounted"
 
 const logQueries = (...args: [...string[], Query[]]) => {
   const queries = args.pop() as Query[]
@@ -17,7 +18,13 @@ const logQueries = (...args: [...string[], Query[]]) => {
   )
 }
 
-const PREFETCH_EXEMPT_QUERIES = [["userMe"]]
+const PREFETCH_EXEMPT_QUERIES = [
+  ["userMe"],
+  ["userLists", "membershipList", "membershipList"],
+  ["learningPaths", "membershipList", "membershipList"],
+]
+
+const RETRIES = process.env.JEST_WORKER_ID ? 1 : 10
 
 /**
  * Call this as high as possible in render tree to detect query usage on
@@ -39,13 +46,23 @@ export const usePrefetchWarnings = ({
    */
   exemptions?: QueryKey[]
 }) => {
+  const mounted = useMounted()
+  const [count, setCount] = useState(0)
+  const [potentialWarnings, setPotentialWarnings] = useState(true)
+
+  useEffect(() => {
+    if ((potentialWarnings && count < RETRIES) || count === RETRIES - 1) {
+      setTimeout(() => setCount(count + 1), 250)
+    }
+  }, [count, potentialWarnings])
+
   /**
    * NOTE: React renders components top-down, but effects run bottom-up, so
    * this effect will run after all child effects.
    */
   useEffect(
     () => {
-      if (process.env.NODE_ENV === "production") {
+      if (process.env.NODE_ENV === "production" || !mounted) {
         return
       }
 
@@ -63,7 +80,7 @@ export const usePrefetchWarnings = ({
           !query.isDisabled(),
       )
 
-      if (potentialPrefetches.length > 0) {
+      if (potentialPrefetches.length > 0 && count === RETRIES) {
         logQueries(
           "The following queries were requested in first render but not prefetched.",
           "If these queries are user-specific, they cannot be prefetched - responses are cached on public CDN.",
@@ -80,17 +97,21 @@ export const usePrefetchWarnings = ({
           !query.isDisabled(),
       )
 
-      if (unusedPrefetches.length > 0) {
+      if (unusedPrefetches.length > 0 && count === RETRIES) {
         logQueries(
           "The following queries were prefetched on the server but not accessed during initial render.",
           "If these queries are no longer in use they should removed from prefetch:",
           unusedPrefetches,
         )
       }
+
+      setPotentialWarnings(
+        potentialPrefetches.length > 0 || unusedPrefetches.length > 0,
+      )
     },
     // We only want to run this on initial render.
     // (Aside: queryClient should be a singleton anyway)
     // eslint-disable-next-line react-hooks/exhaustive-deps
-    [],
+    [mounted, count],
   )
 }
diff --git a/frontends/jest-shared-setup.ts b/frontends/jest-shared-setup.ts
index 1195a262ae..c518ea303f 100644
--- a/frontends/jest-shared-setup.ts
+++ b/frontends/jest-shared-setup.ts
@@ -5,6 +5,7 @@ import { configure } from "@testing-library/react"
 import { resetAllWhenMocks } from "jest-when"
 import * as matchers from "jest-extended"
 import { mockRouter } from "ol-test-utilities/mocks/nextNavigation"
+import preloadAll from "jest-next-dynamic-ts"
 
 expect.extend(matchers)
 
@@ -85,6 +86,10 @@ jest.mock("next/navigation", () => {
   }
 })
 
+beforeAll(async () => {
+  await preloadAll()
+})
+
 afterEach(() => {
   /**
    * Clear all mock call counts between tests.
diff --git a/frontends/main/src/app-pages/HomePage/HomePage.tsx b/frontends/main/src/app-pages/HomePage/HomePage.tsx
index 562e7d8e47..921b14f53d 100644
--- a/frontends/main/src/app-pages/HomePage/HomePage.tsx
+++ b/frontends/main/src/app-pages/HomePage/HomePage.tsx
@@ -1,16 +1,42 @@
 "use client"
 
-import React from "react"
+import React, { Suspense } from "react"
 import { Container, styled, theme } from "ol-components"
-import HeroSearch from "@/page-components/HeroSearch/HeroSearch"
-import BrowseTopicsSection from "./BrowseTopicsSection"
-import NewsEventsSection from "./NewsEventsSection"
-import TestimonialsSection from "./TestimonialsSection"
-import ResourceCarousel from "@/page-components/ResourceCarousel/ResourceCarousel"
-import PersonalizeSection from "./PersonalizeSection"
 import * as carousels from "./carousels"
 import dynamic from "next/dynamic"
 
+const HeroSearch = dynamic(
+  () => import("@/page-components/HeroSearch/HeroSearch"),
+  { ssr: true },
+)
+
+const TestimonialsSection = dynamic(() => import("./TestimonialsSection"), {
+  ssr: true,
+})
+
+const ResourceCarousel = dynamic(
+  () => import("@/page-components/ResourceCarousel/ResourceCarousel"),
+  { ssr: true },
+)
+
+const PersonalizeSection = dynamic(() => import("./PersonalizeSection"), {
+  ssr: true,
+})
+
+const BrowseTopicsSection = dynamic(() => import("./BrowseTopicsSection"), {
+  ssr: true,
+})
+
+const NewsEventsSection = dynamic(() => import("./NewsEventsSection"), {
+  ssr: true,
+})
+
+const LearningResourceDrawer = dynamic(
+  () =>
+    import("@/page-components/LearningResourceDrawer/LearningResourceDrawer"),
+  { ssr: false },
+)
+
 const FullWidthBackground = styled.div({
   background: "linear-gradient(0deg, #FFF 0%, #E9ECEF 100%);",
   paddingBottom: "80px",
@@ -44,11 +70,6 @@ const StyledContainer = styled(Container)({
   },
 })
 
-const LearningResourceDrawer = dynamic(
-  () =>
-    import("@/page-components/LearningResourceDrawer/LearningResourceDrawer"),
-)
-
 const HomePage: React.FC<{ heroImageIndex: number }> = ({ heroImageIndex }) => {
   return (
     <>
@@ -57,21 +78,25 @@ const HomePage: React.FC<{ heroImageIndex: number }> = ({ heroImageIndex }) => {
         <StyledContainer>
           <HeroSearch imageIndex={heroImageIndex} />
           <section>
-            <FeaturedCoursesCarousel
-              titleComponent="h2"
-              title="Featured Courses"
-              config={carousels.FEATURED_RESOURCES_CAROUSEL}
-            />
+            <Suspense>
+              <FeaturedCoursesCarousel
+                titleComponent="h2"
+                title="Featured Courses"
+                config={carousels.FEATURED_RESOURCES_CAROUSEL}
+              />
+            </Suspense>
           </section>
         </StyledContainer>
       </FullWidthBackground>
       <PersonalizeSection />
       <Container component="section">
-        <MediaCarousel
-          titleComponent="h2"
-          title="Media"
-          config={carousels.MEDIA_CAROUSEL}
-        />
+        <Suspense>
+          <MediaCarousel
+            titleComponent="h2"
+            title="Media"
+            config={carousels.MEDIA_CAROUSEL}
+          />
+        </Suspense>
       </Container>
       <BrowseTopicsSection />
       <TestimonialsSection />
diff --git a/frontends/main/src/page-components/Header/Header.tsx b/frontends/main/src/page-components/Header/Header.tsx
index 93ef5bcdb9..87760096ed 100644
--- a/frontends/main/src/page-components/Header/Header.tsx
+++ b/frontends/main/src/page-components/Header/Header.tsx
@@ -1,14 +1,9 @@
 "use client"
 
 import React, { FunctionComponent } from "react"
+import dynamic from "next/dynamic"
 import type { NavData } from "ol-components"
-import {
-  styled,
-  AppBar,
-  NavDrawer,
-  Toolbar,
-  ActionButtonLink,
-} from "ol-components"
+import { styled, AppBar, Toolbar, ActionButtonLink } from "ol-components"
 import {
   RiSearch2Line,
   RiPencilRulerLine,
@@ -25,7 +20,6 @@ import {
 } from "@remixicon/react"
 import { useToggle } from "ol-utilities"
 import MITLogoLink from "@/components/MITLogoLink/MITLogoLink"
-import UserMenu from "./UserMenu"
 import { MenuButton } from "./MenuButton"
 import {
   DEPARTMENTS,
@@ -43,6 +37,13 @@ import {
 } from "@/common/urls"
 import { useUserMe } from "api/hooks/user"
 
+const NavDrawer = dynamic(
+  () => import("ol-components").then((mod) => mod.NavDrawer),
+  { ssr: false },
+)
+
+const UserMenu = dynamic(() => import("./UserMenu"), { ssr: false })
+
 const Bar = styled(AppBar)(({ theme }) => ({
   padding: "16px 8px",
   backgroundColor: theme.custom.colors.navGray,
diff --git a/frontends/main/src/page-components/ResourceCard/ResourceCard.tsx b/frontends/main/src/page-components/ResourceCard/ResourceCard.tsx
index cba791275e..5652c0c6c8 100644
--- a/frontends/main/src/page-components/ResourceCard/ResourceCard.tsx
+++ b/frontends/main/src/page-components/ResourceCard/ResourceCard.tsx
@@ -12,6 +12,7 @@ import {
 } from "../Dialogs/AddToListDialog"
 import { useResourceDrawerHref } from "../LearningResourceDrawer/useResourceDrawerHref"
 import { useUserMe } from "api/hooks/user"
+import { useMounted } from "api/ssr/useMounted"
 import { LearningResource } from "api"
 import { SignupPopover } from "../SignupPopover/SignupPopover"
 import { useIsUserListMember } from "api/hooks/userLists"
@@ -100,6 +101,7 @@ const ResourceCard: React.FC<ResourceCardProps> = ({
     inLearningPath,
     onClick,
   } = useResourceCard(resource)
+  const mounted = useMounted()
   const CardComponent =
     list && condensed
       ? LearningResourceListCardCondensed
@@ -112,8 +114,8 @@ const ResourceCard: React.FC<ResourceCardProps> = ({
         onClick={onClick}
         resource={resource}
         href={resource ? getDrawerHref(resource.id) : undefined}
-        onAddToLearningPathClick={handleAddToLearningPathClick}
-        onAddToUserListClick={handleAddToUserListClick}
+        onAddToLearningPathClick={mounted ? handleAddToLearningPathClick : null}
+        onAddToUserListClick={mounted ? handleAddToUserListClick : null}
         inUserList={inUserList}
         inLearningPath={inLearningPath}
         {...others}
diff --git a/frontends/package.json b/frontends/package.json
index d59f633dcd..391670885c 100644
--- a/frontends/package.json
+++ b/frontends/package.json
@@ -63,6 +63,7 @@
     "jest-environment-jsdom": "^29.5.0",
     "jest-extended": "^4.0.2",
     "jest-fail-on-console": "^3.3.1",
+    "jest-next-dynamic-ts": "^0.1.1",
     "jest-watch-typeahead": "^2.2.2",
     "jest-when": "^3.6.0",
     "postcss-styled-syntax": "^0.7.0",
diff --git a/yarn.lock b/yarn.lock
index d377da5b01..c2e40d6dae 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6730,8 +6730,8 @@ __metadata:
   linkType: hard
 
 "ai@npm:^4.0.13":
-  version: 4.0.36
-  resolution: "ai@npm:4.0.36"
+  version: 4.0.38
+  resolution: "ai@npm:4.0.38"
   dependencies:
     "@ai-sdk/provider": "npm:1.0.4"
     "@ai-sdk/provider-utils": "npm:2.0.7"
@@ -6747,7 +6747,7 @@ __metadata:
       optional: true
     zod:
       optional: true
-  checksum: 10/01e0ca0ae86a7ad4dd2bfbb28b482f2478a37d4f8cb0db002968ce4b1756c4edc9450d706e2c8755f8cbcc66015ec30b4bfb7c71e35cb93a9eb4f8e3e6ff3625
+  checksum: 10/6baa0020dd7d8d6607cc478c5251b16f987a9f80e2f1966d003eaf376850d698996a1d806aa0e07cfb8d90495f60247e6de1d84eefdbef2363f823e64c15f594
   languageName: node
   linkType: hard
 
@@ -10475,6 +10475,7 @@ __metadata:
     jest-environment-jsdom: "npm:^29.5.0"
     jest-extended: "npm:^4.0.2"
     jest-fail-on-console: "npm:^3.3.1"
+    jest-next-dynamic-ts: "npm:^0.1.1"
     jest-watch-typeahead: "npm:^2.2.2"
     jest-when: "npm:^3.6.0"
     postcss-styled-syntax: "npm:^0.7.0"
@@ -12304,6 +12305,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"jest-next-dynamic-ts@npm:^0.1.1":
+  version: 0.1.1
+  resolution: "jest-next-dynamic-ts@npm:0.1.1"
+  checksum: 10/e8d70c827e46f8f5053559fdad193a84805a241ab888cdbe4905c05c1635302eb4a0d4255327e48b144f338e62f88d3ed09c8dc6df56f79f10eca7968a099850
+  languageName: node
+  linkType: hard
+
 "jest-pnp-resolver@npm:^1.2.2":
   version: 1.2.3
   resolution: "jest-pnp-resolver@npm:1.2.3"