diff --git a/apps/compliance-portal/package.json b/apps/compliance-portal/package.json
index 66552bd58..77f07ef94 100644
--- a/apps/compliance-portal/package.json
+++ b/apps/compliance-portal/package.json
@@ -25,6 +25,8 @@
"relay-runtime": "^21.0.1"
},
"devDependencies": {
+ "@babel/core": "^7.29.0",
+ "@rolldown/plugin-babel": "^0.2.3",
"@tailwindcss/vite": "^4.3.1",
"@types/node": "^25.9.3",
"@types/react": "^19.2.17",
diff --git a/apps/compliance-portal/src/App.tsx b/apps/compliance-portal/src/App.tsx
index c63f49ac8..85ffa6398 100644
--- a/apps/compliance-portal/src/App.tsx
+++ b/apps/compliance-portal/src/App.tsx
@@ -14,8 +14,13 @@
import { RouterProvider } from "react-router";
+import { RelayProvider } from "#/lib/relay/RelayProvider";
import { router } from "#/routes";
export function App() {
- return ;
+ return (
+
+
+
+ );
}
diff --git a/apps/compliance-portal/src/components/TopBar/TopBar.tsx b/apps/compliance-portal/src/components/TopBar/TopBar.tsx
new file mode 100644
index 000000000..35d7edd33
--- /dev/null
+++ b/apps/compliance-portal/src/components/TopBar/TopBar.tsx
@@ -0,0 +1,116 @@
+// Copyright (c) 2026 Probo Inc .
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+import { LockSimpleIcon } from "@phosphor-icons/react";
+import { Avatar } from "@probo/ui/src/v2/Avatar/Avatar";
+import { Button } from "@probo/ui/src/v2/Button/Button";
+import { Link } from "@probo/ui/src/v2/Button/Link";
+import { Text } from "@probo/ui/src/v2/typography/Text";
+import { graphql, useFragment } from "react-relay";
+import { Link as RouterLink, useLocation } from "react-router";
+
+import type { TopBar_query$key } from "./__generated__/TopBar_query.graphql";
+import { TopBarUserMenu } from "./TopBarUserMenu";
+import { topBar } from "./variants";
+
+const NAV_ITEMS = [
+ { to: "/documents", label: "Documents" },
+ { to: "/subprocessors", label: "Subprocessors" },
+ { to: "/updates", label: "Updates" },
+ { to: "/requests", label: "Requests" },
+] as const;
+
+const topBarFragment = graphql`
+ fragment TopBar_query on Query {
+ viewer {
+ id
+ ...TopBarUserMenu_identity
+ }
+ currentTrustCenter @required(action: THROW) {
+ organization {
+ name
+ logo {
+ downloadUrl
+ }
+ }
+ }
+ }
+`;
+
+interface TopBarProps {
+ queryKey: TopBar_query$key;
+}
+
+function isActive(pathname: string, to: string): boolean {
+ return pathname === to || pathname.startsWith(`${to}/`);
+}
+
+export function TopBar({ queryKey }: TopBarProps) {
+ const data = useFragment(topBarFragment, queryKey);
+ const { pathname } = useLocation();
+
+ const { organization } = data.currentTrustCenter;
+ const organizationName = organization.name;
+ const logoUrl = organization.logo?.downloadUrl ?? undefined;
+
+ const slots = topBar();
+
+ return (
+
+
+
+ );
+}
diff --git a/apps/compliance-portal/src/components/TopBar/TopBarSkeleton.tsx b/apps/compliance-portal/src/components/TopBar/TopBarSkeleton.tsx
new file mode 100644
index 000000000..88470af70
--- /dev/null
+++ b/apps/compliance-portal/src/components/TopBar/TopBarSkeleton.tsx
@@ -0,0 +1,47 @@
+// Copyright (c) 2026 Probo Inc .
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+import { AvatarSkeleton } from "@probo/ui/src/v2/Avatar/AvatarSkeleton";
+import { ButtonSkeleton } from "@probo/ui/src/v2/Button/ButtonSkeleton";
+import { TextSkeleton } from "@probo/ui/src/v2/typography/TextSkeleton";
+
+import { topBar } from "./variants";
+
+const NAV_ITEM_KEYS = ["documents", "subprocessors", "updates", "requests"] as const;
+
+// Loading placeholder paired with TopBar: reuses the same layout slots with
+// skeleton primitives. Imports no Relay / Base UI, so it renders instantly.
+export function TopBarSkeleton() {
+ const slots = topBar();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/compliance-portal/src/components/TopBar/TopBarUserMenu.tsx b/apps/compliance-portal/src/components/TopBar/TopBarUserMenu.tsx
new file mode 100644
index 000000000..50e27e5ad
--- /dev/null
+++ b/apps/compliance-portal/src/components/TopBar/TopBarUserMenu.tsx
@@ -0,0 +1,71 @@
+// Copyright (c) 2026 Probo Inc .
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+import { CaretDownIcon, SignOutIcon, UserIcon } from "@phosphor-icons/react";
+import { Avatar } from "@probo/ui/src/v2/Avatar/Avatar";
+import { Dropdown } from "@probo/ui/src/v2/Dropdown/Dropdown";
+import { DropdownGroupLabel } from "@probo/ui/src/v2/Dropdown/DropdownGroupLabel";
+import { DropdownItem } from "@probo/ui/src/v2/Dropdown/DropdownItem";
+import { DropdownPopup } from "@probo/ui/src/v2/Dropdown/DropdownPopup";
+import { DropdownSeparator } from "@probo/ui/src/v2/Dropdown/DropdownSeparator";
+import { DropdownTrigger } from "@probo/ui/src/v2/Dropdown/DropdownTrigger";
+import { Text } from "@probo/ui/src/v2/typography/Text";
+import { graphql, useFragment } from "react-relay";
+
+import type { TopBarUserMenu_identity$key } from "./__generated__/TopBarUserMenu_identity.graphql";
+import { topBarUserMenuTrigger } from "./variants";
+
+const topBarUserMenuFragment = graphql`
+ fragment TopBarUserMenu_identity on Identity {
+ fullName
+ email
+ }
+`;
+
+interface TopBarUserMenuProps {
+ identityKey: TopBarUserMenu_identity$key;
+}
+
+export function TopBarUserMenu({ identityKey }: TopBarUserMenuProps) {
+ const identity = useFragment(topBarUserMenuFragment, identityKey);
+
+ return (
+
+
+ }
+ />
+
+ {identity.fullName}
+
+
+
+ )}
+ />
+
+ {identity.email}
+
+ }>
+ Sign out
+
+
+
+ );
+}
diff --git a/apps/compliance-portal/src/components/TopBar/variants.ts b/apps/compliance-portal/src/components/TopBar/variants.ts
new file mode 100644
index 000000000..5a1dcfef4
--- /dev/null
+++ b/apps/compliance-portal/src/components/TopBar/variants.ts
@@ -0,0 +1,38 @@
+// Copyright (c) 2026 Probo Inc .
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+import { tv } from "tailwind-variants/lite";
+
+// Trust Center top navigation bar. Slots are shared by the live TopBar and its
+// skeleton so the loading placeholder is structurally identical.
+export const topBar = tv({
+ slots: {
+ bar: "flex h-14 items-center bg-sand-1 px-8",
+ inner: "mx-auto flex w-full max-w-[1024px] items-center gap-10",
+ brand: "flex items-center gap-2",
+ logo: "shrink-0",
+ spacer: "h-px flex-1",
+ nav: "flex items-center gap-1",
+ },
+});
+
+// Rounded pill that opens the authenticated user menu.
+export const topBarUserMenuTrigger = tv({
+ base: [
+ "flex h-8 items-center gap-2 rounded-full py-1 pr-2.5 pl-1",
+ "cursor-pointer outline-none transition-colors select-none",
+ "hover:bg-sand-3 data-[popup-open]:bg-sand-3",
+ "focus-visible:ring-2 focus-visible:ring-sand-8 focus-visible:ring-offset-1 focus-visible:ring-offset-sand-1",
+ ],
+});
diff --git a/apps/compliance-portal/src/lib/relay/RelayProvider.tsx b/apps/compliance-portal/src/lib/relay/RelayProvider.tsx
new file mode 100644
index 000000000..a20559b5f
--- /dev/null
+++ b/apps/compliance-portal/src/lib/relay/RelayProvider.tsx
@@ -0,0 +1,26 @@
+// Copyright (c) 2026 Probo Inc .
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+import type { PropsWithChildren } from "react";
+import { RelayEnvironmentProvider } from "react-relay";
+
+import { environment } from "#/lib/relay/environment";
+
+export function RelayProvider({ children }: PropsWithChildren) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/apps/compliance-portal/src/lib/relay/environment.ts b/apps/compliance-portal/src/lib/relay/environment.ts
new file mode 100644
index 000000000..553cdd893
--- /dev/null
+++ b/apps/compliance-portal/src/lib/relay/environment.ts
@@ -0,0 +1,29 @@
+// Copyright (c) 2026 Probo Inc .
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+import { makeFetchQuery } from "@probo/relay";
+import { Environment, Network, RecordSource, Store } from "relay-runtime";
+
+import { buildEndpoint } from "#/lib/http/endpoint";
+
+const store = new Store(new RecordSource(), {
+ queryCacheExpirationTime: 1 * 60 * 1000,
+ gcReleaseBufferSize: 20,
+});
+
+export const environment = new Environment({
+ configName: "complianceportal",
+ network: Network.create(makeFetchQuery(buildEndpoint())),
+ store,
+});
diff --git a/apps/compliance-portal/src/pages/MainLayout.tsx b/apps/compliance-portal/src/pages/MainLayout.tsx
index 2111cf97c..a810670ff 100644
--- a/apps/compliance-portal/src/pages/MainLayout.tsx
+++ b/apps/compliance-portal/src/pages/MainLayout.tsx
@@ -12,11 +12,30 @@
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+import type { PreloadedQuery } from "react-relay";
+import { graphql, usePreloadedQuery } from "react-relay";
import { Outlet } from "react-router";
-export default function MainLayout() {
+import { TopBar } from "#/components/TopBar/TopBar";
+
+import type { MainLayoutQuery } from "./__generated__/MainLayoutQuery.graphql";
+
+export const mainLayoutQuery = graphql`
+ query MainLayoutQuery {
+ ...TopBar_query
+ }
+`;
+
+interface MainLayoutProps {
+ queryRef: PreloadedQuery;
+}
+
+export function MainLayout({ queryRef }: MainLayoutProps) {
+ const data = usePreloadedQuery(mainLayoutQuery, queryRef);
+
return (
+
);
diff --git a/apps/compliance-portal/src/pages/MainLayoutLoader.tsx b/apps/compliance-portal/src/pages/MainLayoutLoader.tsx
new file mode 100644
index 000000000..bdb111449
--- /dev/null
+++ b/apps/compliance-portal/src/pages/MainLayoutLoader.tsx
@@ -0,0 +1,34 @@
+// Copyright (c) 2026 Probo Inc .
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+import { useEffect } from "react";
+import { useQueryLoader } from "react-relay";
+
+import type { MainLayoutQuery } from "./__generated__/MainLayoutQuery.graphql";
+import { MainLayout, mainLayoutQuery } from "./MainLayout";
+import { MainLayoutSkeleton } from "./MainLayoutSkeleton";
+
+export default function MainLayoutLoader() {
+ const [queryRef, loadQuery] = useQueryLoader(mainLayoutQuery);
+
+ useEffect(() => {
+ loadQuery({});
+ }, [loadQuery]);
+
+ if (!queryRef) {
+ return ;
+ }
+
+ return ;
+}
diff --git a/apps/compliance-portal/src/pages/MainLayoutSkeleton.tsx b/apps/compliance-portal/src/pages/MainLayoutSkeleton.tsx
new file mode 100644
index 000000000..74d354850
--- /dev/null
+++ b/apps/compliance-portal/src/pages/MainLayoutSkeleton.tsx
@@ -0,0 +1,23 @@
+// Copyright (c) 2026 Probo Inc .
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+import { TopBarSkeleton } from "#/components/TopBar/TopBarSkeleton";
+
+export function MainLayoutSkeleton() {
+ return (
+
+
+
+ );
+}
diff --git a/apps/compliance-portal/src/pages/PlaceholderPage.tsx b/apps/compliance-portal/src/pages/PlaceholderPage.tsx
new file mode 100644
index 000000000..a9b3c0e29
--- /dev/null
+++ b/apps/compliance-portal/src/pages/PlaceholderPage.tsx
@@ -0,0 +1,30 @@
+// Copyright (c) 2026 Probo Inc .
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+import { Heading } from "@probo/ui/src/v2/typography/Heading";
+import { useLocation } from "react-router";
+
+// Temporary stub for the Trust Center sections so the top-bar nav links resolve
+// and the active state renders. Real pages replace these per section.
+export default function PlaceholderPage() {
+ const { pathname } = useLocation();
+ const segment = pathname.replace(/^\//, "").split("/")[0] ?? "";
+ const title = segment.charAt(0).toUpperCase() + segment.slice(1);
+
+ return (
+
+ {title}
+
+ );
+}
diff --git a/apps/compliance-portal/src/routes.tsx b/apps/compliance-portal/src/routes.tsx
index 216df1a77..d37a610ee 100644
--- a/apps/compliance-portal/src/routes.tsx
+++ b/apps/compliance-portal/src/routes.tsx
@@ -17,19 +17,40 @@ import { type AppRoute, routeFromAppRoute } from "@probo/routes";
import { createBrowserRouter } from "react-router";
import { PageSkeleton } from "#/components/skeletons/PageSkeleton";
+import { getPathPrefix } from "#/lib/http/pathPrefix";
const routes = [
{
path: "/",
Fallback: PageSkeleton,
- Component: lazy(() => import("#/pages/MainLayout")),
+ Component: lazy(() => import("#/pages/MainLayoutLoader")),
children: [
{
index: true,
Component: lazy(() => import("#/pages/HomePage")),
},
+ {
+ path: "documents",
+ Component: lazy(() => import("#/pages/PlaceholderPage")),
+ },
+ {
+ path: "subprocessors",
+ Component: lazy(() => import("#/pages/PlaceholderPage")),
+ },
+ {
+ path: "updates",
+ Component: lazy(() => import("#/pages/PlaceholderPage")),
+ },
+ {
+ path: "requests",
+ Component: lazy(() => import("#/pages/PlaceholderPage")),
+ },
],
},
] satisfies AppRoute[];
-export const router = createBrowserRouter(routes.map(routeFromAppRoute));
+// The portal is served under a /trust/{slug} path prefix (or a bare custom
+// domain). Match the router basename to that prefix so the routes resolve.
+export const router = createBrowserRouter(routes.map(routeFromAppRoute), {
+ basename: getPathPrefix() || "/",
+});
diff --git a/apps/compliance-portal/vite.config.ts b/apps/compliance-portal/vite.config.ts
index b19801ff5..dc8c25959 100644
--- a/apps/compliance-portal/vite.config.ts
+++ b/apps/compliance-portal/vite.config.ts
@@ -14,13 +14,16 @@
import { fileURLToPath, URL } from "node:url";
+import babel from "@rolldown/plugin-babel";
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
// https://vite.dev/config/
+// @vitejs/plugin-react@6 (Vite 8) no longer runs Babel, so the Relay tagged
+// template transform is applied via @rolldown/plugin-babel instead.
export default defineConfig({
- plugins: [react(), tailwindcss()],
+ plugins: [react(), babel({ plugins: ["relay"] }), tailwindcss()],
build: {
assetsDir: "assets",
},
diff --git a/package-lock.json b/package-lock.json
index c063dc29d..809727c1e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -44,6 +44,8 @@
"relay-runtime": "^21.0.1"
},
"devDependencies": {
+ "@babel/core": "^7.29.0",
+ "@rolldown/plugin-babel": "^0.2.3",
"@tailwindcss/vite": "^4.3.1",
"@types/node": "^25.9.3",
"@types/react": "^19.2.17",