From 81eb38a24d3bd5c62bfecc7d5d2a721c751bae33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 5 Apr 2026 20:07:42 +0200 Subject: [PATCH 1/2] Fix Leaflet SSR error: rename to .client.tsx Leaflet accesses `window` on import, crashing during SSR. Renamed RouteMapThumbnail to .client.tsx so React Router excludes it from the server bundle entirely. Updated all lazy() imports to match. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../{RouteMapThumbnail.tsx => RouteMapThumbnail.client.tsx} | 0 apps/journal/app/routes/activities.$id.tsx | 2 +- apps/journal/app/routes/activities._index.tsx | 2 +- apps/journal/app/routes/routes.$id.tsx | 2 +- apps/journal/app/routes/routes._index.tsx | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename apps/journal/app/components/{RouteMapThumbnail.tsx => RouteMapThumbnail.client.tsx} (100%) diff --git a/apps/journal/app/components/RouteMapThumbnail.tsx b/apps/journal/app/components/RouteMapThumbnail.client.tsx similarity index 100% rename from apps/journal/app/components/RouteMapThumbnail.tsx rename to apps/journal/app/components/RouteMapThumbnail.client.tsx diff --git a/apps/journal/app/routes/activities.$id.tsx b/apps/journal/app/routes/activities.$id.tsx index 2ddb6f5..8907c24 100644 --- a/apps/journal/app/routes/activities.$id.tsx +++ b/apps/journal/app/routes/activities.$id.tsx @@ -9,7 +9,7 @@ import { listRoutes } from "~/lib/routes.server"; import { ClientDate } from "~/components/ClientDate"; const RouteMapThumbnail = lazy(() => - import("~/components/RouteMapThumbnail").then((m) => ({ default: m.RouteMapThumbnail })), + import("~/components/RouteMapThumbnail.client").then((m) => ({ default: m.RouteMapThumbnail })), ); export async function loader({ params, request }: Route.LoaderArgs) { diff --git a/apps/journal/app/routes/activities._index.tsx b/apps/journal/app/routes/activities._index.tsx index 6a4daef..903ce6a 100644 --- a/apps/journal/app/routes/activities._index.tsx +++ b/apps/journal/app/routes/activities._index.tsx @@ -7,7 +7,7 @@ import { listActivities } from "~/lib/activities.server"; import { ClientDate } from "~/components/ClientDate"; const RouteMapThumbnail = lazy(() => - import("~/components/RouteMapThumbnail").then((m) => ({ default: m.RouteMapThumbnail })), + import("~/components/RouteMapThumbnail.client").then((m) => ({ default: m.RouteMapThumbnail })), ); export async function loader({ request }: Route.LoaderArgs) { diff --git a/apps/journal/app/routes/routes.$id.tsx b/apps/journal/app/routes/routes.$id.tsx index 8773c2c..c78cb14 100644 --- a/apps/journal/app/routes/routes.$id.tsx +++ b/apps/journal/app/routes/routes.$id.tsx @@ -7,7 +7,7 @@ import { getRoute, getRouteWithVersions, deleteRoute, updateRoute } from "~/lib/ import { ClientDate } from "~/components/ClientDate"; const RouteMapThumbnail = lazy(() => - import("~/components/RouteMapThumbnail").then((m) => ({ default: m.RouteMapThumbnail })), + import("~/components/RouteMapThumbnail.client").then((m) => ({ default: m.RouteMapThumbnail })), ); diff --git a/apps/journal/app/routes/routes._index.tsx b/apps/journal/app/routes/routes._index.tsx index 1f4210c..8cb7ae8 100644 --- a/apps/journal/app/routes/routes._index.tsx +++ b/apps/journal/app/routes/routes._index.tsx @@ -7,7 +7,7 @@ import { listRoutes } from "~/lib/routes.server"; import { ClientDate } from "~/components/ClientDate"; const RouteMapThumbnail = lazy(() => - import("~/components/RouteMapThumbnail").then((m) => ({ default: m.RouteMapThumbnail })), + import("~/components/RouteMapThumbnail.client").then((m) => ({ default: m.RouteMapThumbnail })), ); export async function loader({ request }: Route.LoaderArgs) { From 9b2adebc4dc5d06eac488426ea5bc367965c98ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 5 Apr 2026 20:10:57 +0200 Subject: [PATCH 2/2] Add ClientMap wrapper to avoid lazy() SSR resolution .client.tsx modules return undefined on the server, but lazy() still tries to resolve them during SSR causing "Element type is invalid". Replace lazy()+Suspense with a ClientMap component that dynamically imports the map only in useEffect (client-side only). Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/journal/app/components/ClientMap.tsx | 21 +++++++++++++++++++ apps/journal/app/routes/activities.$id.tsx | 10 ++------- apps/journal/app/routes/activities._index.tsx | 10 ++------- apps/journal/app/routes/routes.$id.tsx | 11 +++------- apps/journal/app/routes/routes._index.tsx | 10 ++------- 5 files changed, 30 insertions(+), 32 deletions(-) create mode 100644 apps/journal/app/components/ClientMap.tsx diff --git a/apps/journal/app/components/ClientMap.tsx b/apps/journal/app/components/ClientMap.tsx new file mode 100644 index 0000000..774bf1b --- /dev/null +++ b/apps/journal/app/components/ClientMap.tsx @@ -0,0 +1,21 @@ +import { useState, useEffect, type ComponentProps } from "react"; + +type MapProps = ComponentProps; + +export function ClientMap(props: MapProps) { + const [Component, setComponent] = useState>(); + + useEffect(() => { + import("./RouteMapThumbnail.client").then((m) => setComponent(() => m.RouteMapThumbnail)); + }, []); + + if (!Component) { + return ( +
+ Loading map... +
+ ); + } + + return ; +} diff --git a/apps/journal/app/routes/activities.$id.tsx b/apps/journal/app/routes/activities.$id.tsx index 8907c24..1b80106 100644 --- a/apps/journal/app/routes/activities.$id.tsx +++ b/apps/journal/app/routes/activities.$id.tsx @@ -1,4 +1,3 @@ -import { Suspense, lazy } from "react"; import { data, redirect } from "react-router"; import { useTranslation } from "react-i18next"; import type { Route } from "./+types/activities.$id"; @@ -7,10 +6,7 @@ import { getActivity, deleteActivity, linkActivityToRoute, createRouteFromActivi import { deleteImportByActivity } from "~/lib/sync/imports.server"; import { listRoutes } from "~/lib/routes.server"; import { ClientDate } from "~/components/ClientDate"; - -const RouteMapThumbnail = lazy(() => - import("~/components/RouteMapThumbnail.client").then((m) => ({ default: m.RouteMapThumbnail })), -); +import { ClientMap } from "~/components/ClientMap"; export async function loader({ params, request }: Route.LoaderArgs) { const activity = await getActivity(params.id); @@ -125,9 +121,7 @@ export default function ActivityDetailPage({ loaderData }: Route.ComponentProps) {activity.geojson && (
- Loading map...
}> - - + )} diff --git a/apps/journal/app/routes/activities._index.tsx b/apps/journal/app/routes/activities._index.tsx index 903ce6a..4380be9 100644 --- a/apps/journal/app/routes/activities._index.tsx +++ b/apps/journal/app/routes/activities._index.tsx @@ -1,14 +1,10 @@ import { data, redirect } from "react-router"; -import { Suspense, lazy } from "react"; import { useTranslation } from "react-i18next"; import type { Route } from "./+types/activities._index"; import { getSessionUser } from "~/lib/auth.server"; import { listActivities } from "~/lib/activities.server"; import { ClientDate } from "~/components/ClientDate"; - -const RouteMapThumbnail = lazy(() => - import("~/components/RouteMapThumbnail.client").then((m) => ({ default: m.RouteMapThumbnail })), -); +import { ClientMap } from "~/components/ClientMap"; export async function loader({ request }: Route.LoaderArgs) { const user = await getSessionUser(request); @@ -64,9 +60,7 @@ export default function ActivitiesListPage({ loaderData }: Route.ComponentProps)
{activity.geojson ? ( - }> - - + ) : (
{t("routes.noMapPreview")} diff --git a/apps/journal/app/routes/routes.$id.tsx b/apps/journal/app/routes/routes.$id.tsx index c78cb14..662ba9b 100644 --- a/apps/journal/app/routes/routes.$id.tsx +++ b/apps/journal/app/routes/routes.$id.tsx @@ -1,14 +1,11 @@ -import { useState, useCallback, Suspense, lazy } from "react"; +import { useState, useCallback } from "react"; import { data, redirect } from "react-router"; import { useTranslation } from "react-i18next"; import type { Route } from "./+types/routes.$id"; import { getSessionUser } from "~/lib/auth.server"; import { getRoute, getRouteWithVersions, deleteRoute, updateRoute } from "~/lib/routes.server"; import { ClientDate } from "~/components/ClientDate"; - -const RouteMapThumbnail = lazy(() => - import("~/components/RouteMapThumbnail.client").then((m) => ({ default: m.RouteMapThumbnail })), -); +import { ClientMap } from "~/components/ClientMap"; export async function loader({ params, request }: Route.LoaderArgs) { @@ -161,9 +158,7 @@ export default function RouteDetailPage({ loaderData }: Route.ComponentProps) { {route.geojson && (
- Loading map...
}> - - +
)} diff --git a/apps/journal/app/routes/routes._index.tsx b/apps/journal/app/routes/routes._index.tsx index 8cb7ae8..85511a6 100644 --- a/apps/journal/app/routes/routes._index.tsx +++ b/apps/journal/app/routes/routes._index.tsx @@ -1,14 +1,10 @@ import { data, redirect } from "react-router"; -import { Suspense, lazy } from "react"; import { useTranslation } from "react-i18next"; import type { Route } from "./+types/routes._index"; import { getSessionUser } from "~/lib/auth.server"; import { listRoutes } from "~/lib/routes.server"; import { ClientDate } from "~/components/ClientDate"; - -const RouteMapThumbnail = lazy(() => - import("~/components/RouteMapThumbnail.client").then((m) => ({ default: m.RouteMapThumbnail })), -); +import { ClientMap } from "~/components/ClientMap"; export async function loader({ request }: Route.LoaderArgs) { const user = await getSessionUser(request); @@ -62,9 +58,7 @@ export default function RoutesListPage({ loaderData }: Route.ComponentProps) {
{route.geojson ? ( - }> - - + ) : (
{t("routes.noMapPreview")}