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] 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")}