diff --git a/console/src/components/ShowCreateBlock.tsx b/console/src/components/ShowCreateBlock.tsx
index 74c00f6b2ad8b..095c6106c2ab8 100644
--- a/console/src/components/ShowCreateBlock.tsx
+++ b/console/src/components/ShowCreateBlock.tsx
@@ -79,3 +79,5 @@ export const ShowCreateBlockInner = ({
return ;
};
+
+export default ShowCreateBlock;
diff --git a/console/src/config/AppConfig.ts b/console/src/config/AppConfig.ts
index fd662e9632358..6a67bd0cdd71c 100644
--- a/console/src/config/AppConfig.ts
+++ b/console/src/config/AppConfig.ts
@@ -160,7 +160,15 @@ export class CloudAppConfig implements IBaseAppConfig {
stackSwitcherEnabled =
!this.isImpersonating && this.#consoleEnvironment !== "production";
- stripePromise = getStripePromise(this.#consoleEnvironment);
+ // Lazy so the Stripe.js script only loads when the billing route reads
+ // this property; never fetched on cold load for other routes.
+ #stripePromise?: ReturnType;
+ get stripePromise() {
+ if (!this.#stripePromise) {
+ this.#stripePromise = getStripePromise(this.#consoleEnvironment);
+ }
+ return this.#stripePromise;
+ }
// Whether query retries are enabled. False for test environments.
// TODO (password-auth): Remove the possibility of defaultStack being "test". Should be able to figure
diff --git a/console/src/config/apiKeys.ts b/console/src/config/apiKeys.ts
index 653e27cc593fe..7491f05101e3b 100644
--- a/console/src/config/apiKeys.ts
+++ b/console/src/config/apiKeys.ts
@@ -7,7 +7,7 @@
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.
-import { loadStripe } from "@stripe/stripe-js";
+import type { Stripe } from "@stripe/stripe-js";
export const getLaunchDarklyKey = (consoleEnv: string) => {
if (consoleEnv === "production") {
@@ -43,12 +43,16 @@ export const getSegmentApiKey = ({
return "dGeQYRjmGVsqDI0KIARrAhTvk1BdJJhk";
};
-export const getStripePromise = (consoleEnv: string) => {
- if (consoleEnv === "production") {
- // Production key
- return loadStripe("pk_live_eILilEpJ7DCwmw9I4JHuBLEB001qSrxuw0");
- }
-
- // Stripe development key
- return loadStripe("pk_test_XUB2NGLtbtzx9HQbTKlP1ZCw00FZqUgrXf");
+export const getStripePromise = async (
+ consoleEnv: string,
+): Promise => {
+ // Dynamic import keeps @stripe/stripe-js out of the main bundle; combined
+ // with the lazy getter on AppConfig.stripePromise, the Stripe.js script
+ // only loads when the billing route is visited.
+ const { loadStripe } = await import("@stripe/stripe-js");
+ const key =
+ consoleEnv === "production"
+ ? "pk_live_eILilEpJ7DCwmw9I4JHuBLEB001qSrxuw0"
+ : "pk_test_XUB2NGLtbtzx9HQbTKlP1ZCw00FZqUgrXf";
+ return loadStripe(key);
};
diff --git a/console/src/hooks/useIntercom.ts b/console/src/hooks/useIntercom.ts
index 046627fb48413..620fd2880efc3 100644
--- a/console/src/hooks/useIntercom.ts
+++ b/console/src/hooks/useIntercom.ts
@@ -7,18 +7,34 @@
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.
-import Intercom from "@intercom/messenger-js-sdk";
import { useEffect } from "react";
import { useIntercomJwt } from "~/api/auth";
const APP_ID = "r8661p0d";
+/**
+ * Loads the Intercom SDK and boots it off the critical path. The SDK module is
+ * dynamically imported (own chunk) and the boot call is scheduled in idle time
+ * so it does not compete with first-paint bandwidth/CPU.
+ */
+function bootIntercomDeferred(config: {
+ app_id: string;
+ intercom_user_jwt?: string;
+}) {
+ const schedule =
+ typeof window.requestIdleCallback === "function"
+ ? (cb: () => void) =>
+ window.requestIdleCallback(cb, { timeout: 3000 })
+ : (cb: () => void) => window.setTimeout(cb, 1000);
+ schedule(async () => {
+ const { default: Intercom } = await import("@intercom/messenger-js-sdk");
+ Intercom(config);
+ });
+}
+
export function useIntercomAnonymous() {
useEffect(() => {
- const anonymousConfig = {
- app_id: APP_ID,
- };
- Intercom(anonymousConfig);
+ bootIntercomDeferred({ app_id: APP_ID });
}, []);
}
@@ -28,7 +44,7 @@ export function useIntercom() {
useEffect(() => {
if (jwt) {
- Intercom({ app_id: APP_ID, intercom_user_jwt: jwt });
+ bootIntercomDeferred({ app_id: APP_ID, intercom_user_jwt: jwt });
}
}, [jwt]);
}
diff --git a/console/src/platform/maintained-objects/ObjectMetadata.tsx b/console/src/platform/maintained-objects/ObjectMetadata.tsx
index b4b77b474964e..eb9dc77ba36b6 100644
--- a/console/src/platform/maintained-objects/ObjectMetadata.tsx
+++ b/console/src/platform/maintained-objects/ObjectMetadata.tsx
@@ -12,12 +12,15 @@ import React from "react";
import { ShowCreateObjectType } from "~/api/materialize/showCreate";
import { LoadingContainer } from "~/components/LoadingContainer";
-import { ShowCreateBlock } from "~/components/ShowCreateBlock";
import { ObjectColumnsList } from "~/platform/object-explorer/ObjectColumns";
import { MaintainedObjectListItem } from "./queries";
import { SourceDiagnostics } from "./SourceDiagnostics";
+const ShowCreateBlock = React.lazy(
+ () => import("~/components/ShowCreateBlock"),
+);
+
export interface ObjectMetadataProps {
item: MaintainedObjectListItem;
}
diff --git a/console/src/platform/object-explorer/ConnectionDetails.tsx b/console/src/platform/object-explorer/ConnectionDetails.tsx
index 5c46e9abe8bfa..15ea121f2534c 100644
--- a/console/src/platform/object-explorer/ConnectionDetails.tsx
+++ b/console/src/platform/object-explorer/ConnectionDetails.tsx
@@ -25,7 +25,6 @@ import { ConnectionDependency } from "~/api/materialize/object-explorer/connecti
import { ShowCreateObjectType } from "~/api/materialize/showCreate";
import { AppErrorBoundary } from "~/components/AppErrorBoundary";
import { LoadingContainer } from "~/components/LoadingContainer";
-import { ShowCreateBlock } from "~/components/ShowCreateBlock";
import TextLink from "~/components/TextLink";
import { MainContentContainer } from "~/layouts/BaseLayout";
import { MaterializeTheme } from "~/theme";
@@ -37,6 +36,10 @@ import { useConnectionDependencies, useObjectDetails } from "./queries";
import { relativeObjectPath } from "./routerHelpers";
import { ObjectDetailsParams } from "./useSchemaObjectParams";
+const ShowCreateBlock = React.lazy(
+ () => import("~/components/ShowCreateBlock"),
+);
+
const ConnectionDependencyTable = ({
connectionDependencies,
}: {
@@ -126,10 +129,12 @@ export const ConnectionDetailsContent = ({
-
+ }>
+
+
{connectionDependencies.length > 0 && (
import("~/platform/clusters/DataflowVisualizer"),
);
+const ShowCreateBlock = React.lazy(
+ () => import("~/components/ShowCreateBlock"),
+);
+
+const WorkflowGraph = React.lazy(
+ () => import("~/components/WorkflowGraph/WorkflowGraph"),
+);
+
const SIMPLE_OBJECTS_WITH_INDEXES = ["materialized-view", "view", "table"];
const DATAFLOW_VISUALIZER_OBJECTS = ["materialized-view", "index"];
@@ -86,10 +92,12 @@ export const SimpleObjectDetailsContent = ({
sourceType={object?.sourceType}
/>
{canShowCreate && (
-
+ }>
+
+
)}
@@ -194,7 +202,11 @@ export const SimpleObjectDetailRoutes = () => {
{shouldShowWorkflowGraph && (
}
+ element={
+ }>
+
+
+ }
/>
)}
{shouldShowIndexes && (
diff --git a/console/src/platform/object-explorer/SinkDetailRoutes.tsx b/console/src/platform/object-explorer/SinkDetailRoutes.tsx
index dade863ff6ed4..4e7b7f9d135d3 100644
--- a/console/src/platform/object-explorer/SinkDetailRoutes.tsx
+++ b/console/src/platform/object-explorer/SinkDetailRoutes.tsx
@@ -12,7 +12,6 @@ import { Navigate, Outlet, Route, useNavigate } from "react-router-dom";
import { AppErrorBoundary } from "~/components/AppErrorBoundary";
import { LoadingContainer } from "~/components/LoadingContainer";
-import WorkflowGraph from "~/components/WorkflowGraph/WorkflowGraph";
import { Tab } from "~/layouts/BaseLayout";
import { useSinkList } from "~/platform/sinks/queries";
import SinkErrors from "~/platform/sinks/SinkErrors";
@@ -24,6 +23,10 @@ import { SimpleObjectDetailsContainer } from "./SimpleObjectDetailRoutes";
import { useSchemaObjectParams } from "./useSchemaObjectParams";
import { useToastIfObjectNotExtant } from "./useToastIfObjectNotExtant";
+const WorkflowGraph = React.lazy(
+ () => import("~/components/WorkflowGraph/WorkflowGraph"),
+);
+
const WORKFLOW_TAB = {
label: "Workflow",
href: `../workflow`,
@@ -120,7 +123,11 @@ export const SinkDetailRoutes = () => {
} />
}
+ element={
+ }>
+
+
+ }
/>
} />
diff --git a/console/src/platform/object-explorer/SourceDetailRoutes.tsx b/console/src/platform/object-explorer/SourceDetailRoutes.tsx
index 874628fd4d27b..e847800a6dc5e 100644
--- a/console/src/platform/object-explorer/SourceDetailRoutes.tsx
+++ b/console/src/platform/object-explorer/SourceDetailRoutes.tsx
@@ -13,7 +13,6 @@ import { Navigate, Outlet, Route, useNavigate } from "react-router-dom";
import { Source } from "~/api/materialize/source/sourceList";
import { AppErrorBoundary } from "~/components/AppErrorBoundary";
import { LoadingContainer } from "~/components/LoadingContainer";
-import WorkflowGraph from "~/components/WorkflowGraph/WorkflowGraph";
import { Tab } from "~/layouts/BaseLayout";
import { useSourcesList } from "~/platform/sources/queries";
import SourceErrors from "~/platform/sources/SourceErrors";
@@ -29,6 +28,10 @@ import { SimpleObjectDetailsContainer } from "./SimpleObjectDetailRoutes";
import { useSchemaObjectParams } from "./useSchemaObjectParams";
import { useToastIfObjectNotExtant } from "./useToastIfObjectNotExtant";
+const WorkflowGraph = React.lazy(
+ () => import("~/components/WorkflowGraph/WorkflowGraph"),
+);
+
export const SourceOverviewContainer = () => {
return (
@@ -173,7 +176,11 @@ export const SourceDetailRoutes = () => {
} />
}
+ element={
+ }>
+
+
+ }
/>
>
) : (
@@ -183,7 +190,11 @@ export const SourceDetailRoutes = () => {
} />
}
+ element={
+ }>
+
+
+ }
/>
} />
} />