diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx
index 7a71bb68bc54f..d5d61733e8717 100644
--- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx
+++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx
@@ -384,3 +384,7 @@ export const Expressions: React.FC<Props> = (props) => {
     </>
   );
 };
+
+// required for dynamic import
+// eslint-disable-next-line import/no-default-export
+export default Expressions;
diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx
index 2d9524ca158c8..da342f0a45420 100644
--- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx
+++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx
@@ -5,7 +5,6 @@
  */
 
 import { i18n } from '@kbn/i18n';
-import { isNumber } from 'lodash';
 import {
   MetricExpressionParams,
   Comparator,
@@ -106,3 +105,5 @@ export function validateMetricThreshold({
 
   return validationResult;
 }
+
+const isNumber = (value: unknown): value is number => typeof value === 'number';
diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts
index a40cb1eaec50c..6a999a86c99d1 100644
--- a/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts
+++ b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { i18n } from '@kbn/i18n';
+import React from 'react';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types';
-import { Expressions } from './components/expression';
 import { validateMetricThreshold } from './components/validation';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../server/lib/alerting/metric_threshold/types';
@@ -18,7 +18,7 @@ export function createMetricThresholdAlertType(): AlertTypeModel {
       defaultMessage: 'Metric threshold',
     }),
     iconClass: 'bell',
-    alertParamsExpression: Expressions,
+    alertParamsExpression: React.lazy(() => import('./components/expression')),
     validate: validateMetricThreshold,
     defaultActionMessage: i18n.translate(
       'xpack.infra.metrics.alerting.threshold.defaultActionMessage',
diff --git a/x-pack/plugins/infra/public/apps/common_providers.tsx b/x-pack/plugins/infra/public/apps/common_providers.tsx
new file mode 100644
index 0000000000000..facb0f1539a10
--- /dev/null
+++ b/x-pack/plugins/infra/public/apps/common_providers.tsx
@@ -0,0 +1,47 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { CoreStart } from 'kibana/public';
+import { ApolloClient } from 'apollo-client';
+import {
+  useUiSetting$,
+  KibanaContextProvider,
+} from '../../../../../src/plugins/kibana_react/public';
+import { TriggersActionsProvider } from '../utils/triggers_actions_context';
+import { ClientPluginDeps } from '../types';
+import { TriggersAndActionsUIPublicPluginStart } from '../../../triggers_actions_ui/public';
+import { ApolloClientContext } from '../utils/apollo_context';
+import { EuiThemeProvider } from '../../../observability/public';
+import { NavigationWarningPromptProvider } from '../utils/navigation_warning_prompt';
+
+export const CommonInfraProviders: React.FC<{
+  apolloClient: ApolloClient<{}>;
+  triggersActionsUI: TriggersAndActionsUIPublicPluginStart;
+}> = ({ apolloClient, children, triggersActionsUI }) => {
+  const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
+
+  return (
+    <TriggersActionsProvider triggersActionsUI={triggersActionsUI}>
+      <ApolloClientContext.Provider value={apolloClient}>
+        <EuiThemeProvider darkMode={darkMode}>
+          <NavigationWarningPromptProvider>{children}</NavigationWarningPromptProvider>
+        </EuiThemeProvider>
+      </ApolloClientContext.Provider>
+    </TriggersActionsProvider>
+  );
+};
+
+export const CoreProviders: React.FC<{
+  core: CoreStart;
+  plugins: ClientPluginDeps;
+}> = ({ children, core, plugins }) => {
+  return (
+    <KibanaContextProvider services={{ ...core, ...plugins }}>
+      <core.i18n.Context>{children}</core.i18n.Context>
+    </KibanaContextProvider>
+  );
+};
diff --git a/x-pack/plugins/infra/public/apps/common_styles.ts b/x-pack/plugins/infra/public/apps/common_styles.ts
new file mode 100644
index 0000000000000..546b83a69035c
--- /dev/null
+++ b/x-pack/plugins/infra/public/apps/common_styles.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export const CONTAINER_CLASSNAME = 'infra-container-element';
+
+export const prepareMountElement = (element: HTMLElement) => {
+  // Ensure the element we're handed from application mounting is assigned a class
+  // for our index.scss styles to apply to.
+  element.classList.add(CONTAINER_CLASSNAME);
+};
diff --git a/x-pack/plugins/infra/public/apps/legacy_app.tsx b/x-pack/plugins/infra/public/apps/legacy_app.tsx
new file mode 100644
index 0000000000000..195f252c6b60f
--- /dev/null
+++ b/x-pack/plugins/infra/public/apps/legacy_app.tsx
@@ -0,0 +1,98 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiErrorBoundary } from '@elastic/eui';
+import { createBrowserHistory, History } from 'history';
+import { AppMountParameters } from 'kibana/public';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { Route, RouteProps, Router, Switch } from 'react-router-dom';
+import url from 'url';
+
+// This exists purely to facilitate legacy app/infra URL redirects.
+// It will be removed in 8.0.0.
+export async function renderApp({ element }: AppMountParameters) {
+  const history = createBrowserHistory();
+
+  ReactDOM.render(<LegacyApp history={history} />, element);
+
+  return () => {
+    ReactDOM.unmountComponentAtNode(element);
+  };
+}
+
+const LegacyApp: React.FunctionComponent<{ history: History<unknown> }> = ({ history }) => {
+  return (
+    <EuiErrorBoundary>
+      <Router history={history}>
+        <Switch>
+          <Route
+            path={'/'}
+            render={({ location }: RouteProps) => {
+              if (!location) {
+                return null;
+              }
+
+              let nextPath = '';
+              let nextBasePath = '';
+              let nextSearch;
+
+              if (
+                location.hash.indexOf('#infrastructure') > -1 ||
+                location.hash.indexOf('#/infrastructure') > -1
+              ) {
+                nextPath = location.hash.replace(
+                  new RegExp(
+                    '#infrastructure/|#/infrastructure/|#/infrastructure|#infrastructure',
+                    'g'
+                  ),
+                  ''
+                );
+                nextBasePath = location.pathname.replace('app/infra', 'app/metrics');
+              } else if (
+                location.hash.indexOf('#logs') > -1 ||
+                location.hash.indexOf('#/logs') > -1
+              ) {
+                nextPath = location.hash.replace(
+                  new RegExp('#logs/|#/logs/|#/logs|#logs', 'g'),
+                  ''
+                );
+                nextBasePath = location.pathname.replace('app/infra', 'app/logs');
+              } else {
+                // This covers /app/infra and /app/infra/home (both of which used to render
+                // the metrics inventory page)
+                nextPath = 'inventory';
+                nextBasePath = location.pathname.replace('app/infra', 'app/metrics');
+                nextSearch = undefined;
+              }
+
+              // app/infra#infrastructure/metrics/:type/:node was changed to app/metrics/detail/:type/:node, this
+              // accounts for that edge case
+              nextPath = nextPath.replace('metrics/', 'detail/');
+
+              // Query parameters (location.search) will arrive as part of location.hash and not location.search
+              const nextPathParts = nextPath.split('?');
+              nextPath = nextPathParts[0];
+              nextSearch = nextPathParts[1] ? nextPathParts[1] : undefined;
+
+              let nextUrl = url.format({
+                pathname: `${nextBasePath}/${nextPath}`,
+                hash: undefined,
+                search: nextSearch,
+              });
+
+              nextUrl = nextUrl.replace('//', '/');
+
+              window.location.href = nextUrl;
+
+              return null;
+            }}
+          />
+        </Switch>
+      </Router>
+    </EuiErrorBoundary>
+  );
+};
diff --git a/x-pack/plugins/infra/public/apps/logs_app.tsx b/x-pack/plugins/infra/public/apps/logs_app.tsx
new file mode 100644
index 0000000000000..e0251522bb24c
--- /dev/null
+++ b/x-pack/plugins/infra/public/apps/logs_app.tsx
@@ -0,0 +1,66 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { ApolloClient } from 'apollo-client';
+import { History } from 'history';
+import { CoreStart } from 'kibana/public';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { Route, Router, Switch } from 'react-router-dom';
+import { AppMountParameters } from '../../../../../src/core/public';
+import '../index.scss';
+import { NotFoundPage } from '../pages/404';
+import { LinkToLogsPage } from '../pages/link_to/link_to_logs';
+import { LogsPage } from '../pages/logs';
+import { ClientPluginDeps } from '../types';
+import { createApolloClient } from '../utils/apollo_client';
+import { CommonInfraProviders, CoreProviders } from './common_providers';
+import { prepareMountElement } from './common_styles';
+
+export const renderApp = (
+  core: CoreStart,
+  plugins: ClientPluginDeps,
+  { element, history }: AppMountParameters
+) => {
+  const apolloClient = createApolloClient(core.http.fetch);
+
+  prepareMountElement(element);
+
+  ReactDOM.render(
+    <LogsApp apolloClient={apolloClient} core={core} history={history} plugins={plugins} />,
+    element
+  );
+
+  return () => {
+    ReactDOM.unmountComponentAtNode(element);
+  };
+};
+
+const LogsApp: React.FC<{
+  apolloClient: ApolloClient<{}>;
+  core: CoreStart;
+  history: History<unknown>;
+  plugins: ClientPluginDeps;
+}> = ({ apolloClient, core, history, plugins }) => {
+  const uiCapabilities = core.application.capabilities;
+
+  return (
+    <CoreProviders core={core} plugins={plugins}>
+      <CommonInfraProviders
+        apolloClient={apolloClient}
+        triggersActionsUI={plugins.triggers_actions_ui}
+      >
+        <Router history={history}>
+          <Switch>
+            <Route path="/link-to" component={LinkToLogsPage} />
+            {uiCapabilities?.logs?.show && <Route path="/" component={LogsPage} />}
+            <Route component={NotFoundPage} />
+          </Switch>
+        </Router>
+      </CommonInfraProviders>
+    </CoreProviders>
+  );
+};
diff --git a/x-pack/plugins/infra/public/apps/metrics_app.tsx b/x-pack/plugins/infra/public/apps/metrics_app.tsx
new file mode 100644
index 0000000000000..8713abe0510a6
--- /dev/null
+++ b/x-pack/plugins/infra/public/apps/metrics_app.tsx
@@ -0,0 +1,82 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { ApolloClient } from 'apollo-client';
+import { History } from 'history';
+import { CoreStart } from 'kibana/public';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { Route, Router, Switch } from 'react-router-dom';
+import { AppMountParameters } from '../../../../../src/core/public';
+import '../index.scss';
+import { NotFoundPage } from '../pages/404';
+import { LinkToMetricsPage } from '../pages/link_to/link_to_metrics';
+import { InfrastructurePage } from '../pages/metrics';
+import { MetricDetail } from '../pages/metrics/metric_detail';
+import { ClientPluginDeps } from '../types';
+import { createApolloClient } from '../utils/apollo_client';
+import { RedirectWithQueryParams } from '../utils/redirect_with_query_params';
+import { CommonInfraProviders, CoreProviders } from './common_providers';
+import { prepareMountElement } from './common_styles';
+
+export const renderApp = (
+  core: CoreStart,
+  plugins: ClientPluginDeps,
+  { element, history }: AppMountParameters
+) => {
+  const apolloClient = createApolloClient(core.http.fetch);
+
+  prepareMountElement(element);
+
+  ReactDOM.render(
+    <MetricsApp apolloClient={apolloClient} core={core} history={history} plugins={plugins} />,
+    element
+  );
+
+  return () => {
+    ReactDOM.unmountComponentAtNode(element);
+  };
+};
+
+const MetricsApp: React.FC<{
+  apolloClient: ApolloClient<{}>;
+  core: CoreStart;
+  history: History<unknown>;
+  plugins: ClientPluginDeps;
+}> = ({ apolloClient, core, history, plugins }) => {
+  const uiCapabilities = core.application.capabilities;
+
+  return (
+    <CoreProviders core={core} plugins={plugins}>
+      <CommonInfraProviders
+        apolloClient={apolloClient}
+        triggersActionsUI={plugins.triggers_actions_ui}
+      >
+        <Router history={history}>
+          <Switch>
+            <Route path="/link-to" component={LinkToMetricsPage} />
+            {uiCapabilities?.infrastructure?.show && (
+              <RedirectWithQueryParams from="/" exact={true} to="/inventory" />
+            )}
+            {uiCapabilities?.infrastructure?.show && (
+              <RedirectWithQueryParams from="/snapshot" exact={true} to="/inventory" />
+            )}
+            {uiCapabilities?.infrastructure?.show && (
+              <RedirectWithQueryParams from="/metrics-explorer" exact={true} to="/explorer" />
+            )}
+            {uiCapabilities?.infrastructure?.show && (
+              <Route path="/detail/:type/:node" component={MetricDetail} />
+            )}
+            {uiCapabilities?.infrastructure?.show && (
+              <Route path="/" component={InfrastructurePage} />
+            )}
+            <Route component={NotFoundPage} />
+          </Switch>
+        </Router>
+      </CommonInfraProviders>
+    </CoreProviders>
+  );
+};
diff --git a/x-pack/plugins/infra/public/apps/start_app.tsx b/x-pack/plugins/infra/public/apps/start_app.tsx
deleted file mode 100644
index 4c213700b62e6..0000000000000
--- a/x-pack/plugins/infra/public/apps/start_app.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { ApolloProvider } from 'react-apollo';
-import { CoreStart, AppMountParameters } from 'kibana/public';
-
-// TODO use theme provided from parentApp when kibana supports it
-import { EuiErrorBoundary } from '@elastic/eui';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { EuiThemeProvider } from '../../../observability/public/typings/eui_styled_components';
-import { InfraFrontendLibs } from '../lib/lib';
-import { ApolloClientContext } from '../utils/apollo_context';
-import { HistoryContext } from '../utils/history_context';
-import {
-  useUiSetting$,
-  KibanaContextProvider,
-} from '../../../../../src/plugins/kibana_react/public';
-import { AppRouter } from '../routers';
-import { TriggersAndActionsUIPublicPluginSetup } from '../../../triggers_actions_ui/public';
-import { TriggersActionsProvider } from '../utils/triggers_actions_context';
-import '../index.scss';
-import { NavigationWarningPromptProvider } from '../utils/navigation_warning_prompt';
-
-export const CONTAINER_CLASSNAME = 'infra-container-element';
-
-export async function startApp(
-  libs: InfraFrontendLibs,
-  core: CoreStart,
-  plugins: object,
-  params: AppMountParameters,
-  Router: AppRouter,
-  triggersActionsUI: TriggersAndActionsUIPublicPluginSetup
-) {
-  const { element, history } = params;
-
-  const InfraPluginRoot: React.FunctionComponent = () => {
-    const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
-
-    return (
-      <core.i18n.Context>
-        <EuiErrorBoundary>
-          <TriggersActionsProvider triggersActionsUI={triggersActionsUI}>
-            <ApolloProvider client={libs.apolloClient}>
-              <ApolloClientContext.Provider value={libs.apolloClient}>
-                <EuiThemeProvider darkMode={darkMode}>
-                  <HistoryContext.Provider value={history}>
-                    <NavigationWarningPromptProvider>
-                      <Router history={history} />
-                    </NavigationWarningPromptProvider>
-                  </HistoryContext.Provider>
-                </EuiThemeProvider>
-              </ApolloClientContext.Provider>
-            </ApolloProvider>
-          </TriggersActionsProvider>
-        </EuiErrorBoundary>
-      </core.i18n.Context>
-    );
-  };
-
-  const App: React.FunctionComponent = () => (
-    <KibanaContextProvider services={{ ...core, ...plugins }}>
-      <InfraPluginRoot />
-    </KibanaContextProvider>
-  );
-
-  // Ensure the element we're handed from application mounting is assigned a class
-  // for our index.scss styles to apply to.
-  element.className += ` ${CONTAINER_CLASSNAME}`;
-
-  ReactDOM.render(<App />, element);
-
-  return () => {
-    ReactDOM.unmountComponentAtNode(element);
-  };
-}
diff --git a/x-pack/plugins/infra/public/apps/start_legacy_app.tsx b/x-pack/plugins/infra/public/apps/start_legacy_app.tsx
deleted file mode 100644
index 6e5960ceb2081..0000000000000
--- a/x-pack/plugins/infra/public/apps/start_legacy_app.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createBrowserHistory } from 'history';
-import React from 'react';
-import url from 'url';
-import ReactDOM from 'react-dom';
-import { AppMountParameters } from 'kibana/public';
-import { Route, Router, Switch, RouteProps } from 'react-router-dom';
-// TODO use theme provided from parentApp when kibana supports it
-import { EuiErrorBoundary } from '@elastic/eui';
-
-// This exists purely to facilitate legacy app/infra URL redirects.
-// It will be removed in 8.0.0.
-export async function startLegacyApp(params: AppMountParameters) {
-  const { element } = params;
-  const history = createBrowserHistory();
-
-  const App: React.FunctionComponent = () => {
-    return (
-      <EuiErrorBoundary>
-        <Router history={history}>
-          <Switch>
-            <Route
-              path={'/'}
-              render={({ location }: RouteProps) => {
-                if (!location) {
-                  return null;
-                }
-
-                let nextPath = '';
-                let nextBasePath = '';
-                let nextSearch;
-
-                if (
-                  location.hash.indexOf('#infrastructure') > -1 ||
-                  location.hash.indexOf('#/infrastructure') > -1
-                ) {
-                  nextPath = location.hash.replace(
-                    new RegExp(
-                      '#infrastructure/|#/infrastructure/|#/infrastructure|#infrastructure',
-                      'g'
-                    ),
-                    ''
-                  );
-                  nextBasePath = location.pathname.replace('app/infra', 'app/metrics');
-                } else if (
-                  location.hash.indexOf('#logs') > -1 ||
-                  location.hash.indexOf('#/logs') > -1
-                ) {
-                  nextPath = location.hash.replace(
-                    new RegExp('#logs/|#/logs/|#/logs|#logs', 'g'),
-                    ''
-                  );
-                  nextBasePath = location.pathname.replace('app/infra', 'app/logs');
-                } else {
-                  // This covers /app/infra and /app/infra/home (both of which used to render
-                  // the metrics inventory page)
-                  nextPath = 'inventory';
-                  nextBasePath = location.pathname.replace('app/infra', 'app/metrics');
-                  nextSearch = undefined;
-                }
-
-                // app/inra#infrastructure/metrics/:type/:node was changed to app/metrics/detail/:type/:node, this
-                // accounts for that edge case
-                nextPath = nextPath.replace('metrics/', 'detail/');
-
-                // Query parameters (location.search) will arrive as part of location.hash and not location.search
-                const nextPathParts = nextPath.split('?');
-                nextPath = nextPathParts[0];
-                nextSearch = nextPathParts[1] ? nextPathParts[1] : undefined;
-
-                let nextUrl = url.format({
-                  pathname: `${nextBasePath}/${nextPath}`,
-                  hash: undefined,
-                  search: nextSearch,
-                });
-
-                nextUrl = nextUrl.replace('//', '/');
-
-                window.location.href = nextUrl;
-
-                return null;
-              }}
-            />
-          </Switch>
-        </Router>
-      </EuiErrorBoundary>
-    );
-  };
-
-  ReactDOM.render(<App />, element);
-
-  return () => {
-    ReactDOM.unmountComponentAtNode(element);
-  };
-}
diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/expression.tsx b/x-pack/plugins/infra/public/components/alerting/inventory/expression.tsx
index 074464fb55414..ce14897991e60 100644
--- a/x-pack/plugins/infra/public/components/alerting/inventory/expression.tsx
+++ b/x-pack/plugins/infra/public/components/alerting/inventory/expression.tsx
@@ -336,6 +336,10 @@ export const Expressions: React.FC<Props> = (props) => {
   );
 };
 
+// required for dynamic import
+// eslint-disable-next-line import/no-default-export
+export default Expressions;
+
 interface ExpressionRowProps {
   nodeType: InventoryItemType;
   expressionId: number;
diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/metric_inventory_threshold_alert_type.ts b/x-pack/plugins/infra/public/components/alerting/inventory/metric_inventory_threshold_alert_type.ts
index 9ede2d2a47727..0cb564ec2194e 100644
--- a/x-pack/plugins/infra/public/components/alerting/inventory/metric_inventory_threshold_alert_type.ts
+++ b/x-pack/plugins/infra/public/components/alerting/inventory/metric_inventory_threshold_alert_type.ts
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { i18n } from '@kbn/i18n';
+import React from 'react';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { AlertTypeModel } from '../../../../../triggers_actions_ui/public/types';
-import { Expressions } from './expression';
 import { validateMetricThreshold } from './validation';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from '../../../../server/lib/alerting/inventory_metric_threshold/types';
@@ -18,7 +18,7 @@ export function getInventoryMetricAlertType(): AlertTypeModel {
       defaultMessage: 'Inventory',
     }),
     iconClass: 'bell',
-    alertParamsExpression: Expressions,
+    alertParamsExpression: React.lazy(() => import('./expression')),
     validate: validateMetricThreshold,
     defaultActionMessage: i18n.translate(
       'xpack.infra.metrics.alerting.inventory.threshold.defaultActionMessage',
diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/validation.tsx b/x-pack/plugins/infra/public/components/alerting/inventory/validation.tsx
index 441adeec988c7..47ecd3c527fad 100644
--- a/x-pack/plugins/infra/public/components/alerting/inventory/validation.tsx
+++ b/x-pack/plugins/infra/public/components/alerting/inventory/validation.tsx
@@ -6,8 +6,6 @@
 
 import { i18n } from '@kbn/i18n';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { isNumber } from 'lodash';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { MetricExpressionParams } from '../../../../server/lib/alerting/metric_threshold/types';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ValidationResult } from '../../../../../triggers_actions_ui/public/types';
@@ -95,3 +93,5 @@ export function validateMetricThreshold({
 
   return validationResult;
 }
+
+const isNumber = (value: unknown): value is number => typeof value === 'number';
diff --git a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/editor.tsx b/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/editor.tsx
index 609f99805fe9c..a3a48d477425b 100644
--- a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/editor.tsx
+++ b/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/editor.tsx
@@ -236,3 +236,7 @@ export const Editor: React.FC<Props> = (props) => {
     </>
   );
 };
+
+// required for dynamic import
+// eslint-disable-next-line import/no-default-export
+export default Editor;
diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold_alert_type.ts b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold_alert_type.ts
index 9bba8bd804f80..4c7811f0d9666 100644
--- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold_alert_type.ts
+++ b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold_alert_type.ts
@@ -4,10 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { i18n } from '@kbn/i18n';
+import React from 'react';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { AlertTypeModel } from '../../../../../triggers_actions_ui/public/types';
 import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../../common/alerting/logs/types';
-import { ExpressionEditor } from './expression_editor';
 import { validateExpression } from './validation';
 
 export function getAlertType(): AlertTypeModel {
@@ -17,7 +17,7 @@ export function getAlertType(): AlertTypeModel {
       defaultMessage: 'Log threshold',
     }),
     iconClass: 'bell',
-    alertParamsExpression: ExpressionEditor,
+    alertParamsExpression: React.lazy(() => import('./expression_editor/editor')),
     validate: validateExpression,
     defaultActionMessage: i18n.translate(
       'xpack.infra.logs.alerting.threshold.defaultActionMessage',
diff --git a/x-pack/plugins/infra/public/compose_libs.ts b/x-pack/plugins/infra/public/compose_libs.ts
deleted file mode 100644
index f2060983e95eb..0000000000000
--- a/x-pack/plugins/infra/public/compose_libs.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
-import ApolloClient from 'apollo-client';
-import { ApolloLink } from 'apollo-link';
-import { createHttpLink } from 'apollo-link-http';
-import { withClientState } from 'apollo-link-state';
-import { CoreStart, HttpFetchOptions } from 'src/core/public';
-import { InfraFrontendLibs } from './lib/lib';
-import introspectionQueryResultData from './graphql/introspection.json';
-import { InfraKibanaObservableApiAdapter } from './lib/adapters/observable_api/kibana_observable_api';
-
-export function composeLibs(core: CoreStart) {
-  const cache = new InMemoryCache({
-    addTypename: false,
-    fragmentMatcher: new IntrospectionFragmentMatcher({
-      introspectionQueryResultData,
-    }),
-  });
-
-  const observableApi = new InfraKibanaObservableApiAdapter({
-    basePath: core.http.basePath.get(),
-  });
-
-  const wrappedFetch = (path: string, options: HttpFetchOptions) => {
-    return new Promise<Response>(async (resolve, reject) => {
-      // core.http.fetch isn't 100% compatible with the Fetch API and will
-      // throw Errors on 401s. This top level try / catch handles those scenarios.
-      try {
-        core.http
-          .fetch(path, {
-            ...options,
-            // Set headers to undefined due to this bug: https://github.com/apollographql/apollo-link/issues/249,
-            // Apollo will try to set a "content-type" header which will conflict with the "Content-Type" header that
-            // core.http.fetch correctly sets.
-            headers: undefined,
-            asResponse: true,
-          })
-          .then((res) => {
-            if (!res.response) {
-              return reject();
-            }
-            // core.http.fetch will parse the Response and set a body before handing it back. As such .text() / .json()
-            // will have already been called on the Response instance. However, Apollo will also want to call
-            // .text() / .json() on the instance, as it expects the raw Response instance, rather than core's wrapper.
-            // .text() / .json() can only be called once, and an Error will be thrown if those methods are accessed again.
-            // This hacks around that by setting up a new .text() method that will restringify the JSON response we already have.
-            // This does result in an extra stringify / parse cycle, which isn't ideal, but as we only have a few endpoints left using
-            // GraphQL this shouldn't create excessive overhead.
-            // Ref: https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-http/src/httpLink.ts#L134
-            // and
-            // https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-http-common/src/index.ts#L125
-            return resolve({
-              ...res.response,
-              text: () => {
-                return new Promise(async (resolveText, rejectText) => {
-                  if (res.body) {
-                    return resolveText(JSON.stringify(res.body));
-                  } else {
-                    return rejectText();
-                  }
-                });
-              },
-            });
-          });
-      } catch (error) {
-        reject(error);
-      }
-    });
-  };
-
-  const HttpLink = createHttpLink({
-    fetch: wrappedFetch,
-    uri: `/api/infra/graphql`,
-  });
-
-  const graphQLOptions = {
-    cache,
-    link: ApolloLink.from([
-      withClientState({
-        cache,
-        resolvers: {},
-      }),
-      HttpLink,
-    ]),
-  };
-
-  const apolloClient = new ApolloClient(graphQLOptions);
-
-  const libs: InfraFrontendLibs = {
-    apolloClient,
-    observableApi,
-  };
-  return libs;
-}
diff --git a/x-pack/plugins/infra/public/containers/with_state_from_location.tsx b/x-pack/plugins/infra/public/containers/with_state_from_location.tsx
deleted file mode 100644
index 2a9676046d451..0000000000000
--- a/x-pack/plugins/infra/public/containers/with_state_from_location.tsx
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { parse, stringify } from 'query-string';
-import { Location } from 'history';
-import { omit } from 'lodash';
-import React from 'react';
-import { RouteComponentProps, withRouter } from 'react-router-dom';
-// eslint-disable-next-line @typescript-eslint/camelcase
-import { decode_object, encode_object } from 'rison-node';
-import { Omit } from '../lib/lib';
-
-interface AnyObject {
-  [key: string]: any;
-}
-
-interface WithStateFromLocationOptions<StateInLocation> {
-  mapLocationToState: (location: Location) => StateInLocation;
-  mapStateToLocation: (state: StateInLocation, location: Location) => Location;
-}
-
-type InjectedPropsFromLocation<StateInLocation> = Partial<StateInLocation> & {
-  pushStateInLocation?: (state: StateInLocation) => void;
-  replaceStateInLocation?: (state: StateInLocation) => void;
-};
-
-export const withStateFromLocation = <StateInLocation extends {}>({
-  mapLocationToState,
-  mapStateToLocation,
-}: WithStateFromLocationOptions<StateInLocation>) => <
-  WrappedComponentProps extends InjectedPropsFromLocation<StateInLocation>
->(
-  WrappedComponent: React.ComponentType<WrappedComponentProps>
-) => {
-  const wrappedName = WrappedComponent.displayName || WrappedComponent.name;
-
-  return withRouter(
-    class WithStateFromLocation extends React.PureComponent<
-      RouteComponentProps<{}> &
-        Omit<WrappedComponentProps, InjectedPropsFromLocation<StateInLocation>>
-    > {
-      public static displayName = `WithStateFromLocation(${wrappedName})`;
-
-      public render() {
-        const { location } = this.props;
-        const otherProps = omit(this.props, ['location', 'history', 'match', 'staticContext']);
-
-        const stateFromLocation = mapLocationToState(location);
-
-        return (
-          // @ts-ignore
-          <WrappedComponent
-            {...otherProps}
-            {...stateFromLocation}
-            pushStateInLocation={this.pushStateInLocation}
-            replaceStateInLocation={this.replaceStateInLocation}
-          />
-        );
-      }
-
-      private pushStateInLocation = (state: StateInLocation) => {
-        const { history, location } = this.props;
-
-        const newLocation = mapStateToLocation(state, this.props.location);
-
-        if (newLocation !== location) {
-          history.push(newLocation);
-        }
-      };
-
-      private replaceStateInLocation = (state: StateInLocation) => {
-        const { history, location } = this.props;
-
-        const newLocation = mapStateToLocation(state, this.props.location);
-
-        if (newLocation !== location) {
-          history.replace(newLocation);
-        }
-      };
-    }
-  );
-};
-
-const decodeRisonAppState = (queryValues: { _a?: string }): AnyObject => {
-  try {
-    return queryValues && queryValues._a ? decode_object(queryValues._a) : {};
-  } catch (error) {
-    if (error instanceof Error && error.message.startsWith('rison decoder error')) {
-      return {};
-    }
-    throw error;
-  }
-};
-
-const encodeRisonAppState = (state: AnyObject) => ({
-  _a: encode_object(state),
-});
-
-export const mapRisonAppLocationToState = <State extends {}>(
-  mapState: (risonAppState: AnyObject) => State = (state: AnyObject) => state as State
-) => (location: Location): State => {
-  const queryValues = parse(location.search.substring(1), { sort: false });
-  const decodedState = decodeRisonAppState(queryValues);
-  return mapState(decodedState);
-};
-
-export const mapStateToRisonAppLocation = <State extends {}>(
-  mapState: (state: State) => AnyObject = (state: State) => state
-) => (state: State, location: Location): Location => {
-  const previousQueryValues = parse(location.search.substring(1), { sort: false });
-  const previousState = decodeRisonAppState(previousQueryValues);
-
-  const encodedState = encodeRisonAppState({
-    ...previousState,
-    ...mapState(state),
-  });
-  const newQueryValues = stringify(
-    {
-      ...previousQueryValues,
-      ...encodedState,
-    },
-    { sort: false }
-  );
-  return {
-    ...location,
-    search: `?${newQueryValues}`,
-  };
-};
diff --git a/x-pack/plugins/infra/public/graphql/log_entries.gql_query.ts b/x-pack/plugins/infra/public/graphql/log_entries.gql_query.ts
deleted file mode 100644
index 41ff3c293a713..0000000000000
--- a/x-pack/plugins/infra/public/graphql/log_entries.gql_query.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import gql from 'graphql-tag';
-
-import { sharedFragments } from '../../common/graphql/shared';
-
-export const logEntriesQuery = gql`
-  query LogEntries(
-    $sourceId: ID = "default"
-    $timeKey: InfraTimeKeyInput!
-    $countBefore: Int = 0
-    $countAfter: Int = 0
-    $filterQuery: String
-  ) {
-    source(id: $sourceId) {
-      id
-      logEntriesAround(
-        key: $timeKey
-        countBefore: $countBefore
-        countAfter: $countAfter
-        filterQuery: $filterQuery
-      ) {
-        start {
-          ...InfraTimeKeyFields
-        }
-        end {
-          ...InfraTimeKeyFields
-        }
-        hasMoreBefore
-        hasMoreAfter
-        entries {
-          ...InfraLogEntryFields
-        }
-      }
-    }
-  }
-
-  ${sharedFragments.InfraTimeKey}
-  ${sharedFragments.InfraLogEntryFields}
-`;
diff --git a/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx b/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx
index f9cfaf71036f6..d93cc44c45623 100644
--- a/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx
+++ b/x-pack/plugins/infra/public/hooks/use_link_props.test.tsx
@@ -4,15 +4,15 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { encode } from 'rison-node';
-import { createMemoryHistory } from 'history';
 import { renderHook } from '@testing-library/react-hooks';
+import { createMemoryHistory } from 'history';
 import React from 'react';
-import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
-import { HistoryContext } from '../utils/history_context';
+import { Router } from 'react-router-dom';
+import { encode } from 'rison-node';
 import { coreMock } from 'src/core/public/mocks';
-import { useLinkProps, LinkDescriptor } from './use_link_props';
 import { ScopedHistory } from '../../../../../src/core/public';
+import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
+import { LinkDescriptor, useLinkProps } from './use_link_props';
 
 const PREFIX = '/test-basepath/s/test-space/app/';
 
@@ -30,9 +30,9 @@ const scopedHistory = new ScopedHistory(history, `${PREFIX}${INTERNAL_APP}`);
 
 const ProviderWrapper: React.FC = ({ children }) => {
   return (
-    <HistoryContext.Provider value={scopedHistory}>
+    <Router history={scopedHistory}>
       <KibanaContextProvider services={{ ...coreStartMock }}>{children}</KibanaContextProvider>;
-    </HistoryContext.Provider>
+    </Router>
   );
 };
 
diff --git a/x-pack/plugins/infra/public/index.ts b/x-pack/plugins/infra/public/index.ts
index 1dfdf827f203b..8f2d37fa1daa9 100644
--- a/x-pack/plugins/infra/public/index.ts
+++ b/x-pack/plugins/infra/public/index.ts
@@ -4,8 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { PluginInitializerContext, PluginInitializer } from 'kibana/public';
-import { Plugin, ClientSetup, ClientStart, ClientPluginsSetup, ClientPluginsStart } from './plugin';
+import { PluginInitializer, PluginInitializerContext } from 'kibana/public';
+import { ClientSetup, ClientStart, Plugin } from './plugin';
+import { ClientPluginsSetup, ClientPluginsStart } from './types';
 
 export const plugin: PluginInitializer<
   ClientSetup,
diff --git a/x-pack/plugins/infra/public/lib/adapters/observable_api/kibana_observable_api.ts b/x-pack/plugins/infra/public/lib/adapters/observable_api/kibana_observable_api.ts
deleted file mode 100644
index 9ae21d96886f3..0000000000000
--- a/x-pack/plugins/infra/public/lib/adapters/observable_api/kibana_observable_api.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { ajax } from 'rxjs/ajax';
-import { map } from 'rxjs/operators';
-
-import {
-  InfraObservableApi,
-  InfraObservableApiPostParams,
-  InfraObservableApiResponse,
-} from '../../lib';
-
-export class InfraKibanaObservableApiAdapter implements InfraObservableApi {
-  private basePath: string;
-  private defaultHeaders: {
-    [headerName: string]: boolean | string;
-  };
-
-  constructor({ basePath }: { basePath: string }) {
-    this.basePath = basePath;
-    this.defaultHeaders = {
-      'kbn-xsrf': true,
-    };
-  }
-
-  public post = <RequestBody extends {} = {}, ResponseBody extends {} = {}>({
-    url,
-    body,
-  }: InfraObservableApiPostParams<RequestBody>): InfraObservableApiResponse<ResponseBody> =>
-    ajax({
-      body: body ? JSON.stringify(body) : undefined,
-      headers: {
-        ...this.defaultHeaders,
-        'Content-Type': 'application/json',
-      },
-      method: 'POST',
-      responseType: 'json',
-      timeout: 30000,
-      url: `${this.basePath}/api/${url}`,
-      withCredentials: true,
-    }).pipe(map(({ response, status }) => ({ response, status })));
-}
diff --git a/x-pack/plugins/infra/public/lib/lib.ts b/x-pack/plugins/infra/public/lib/lib.ts
index d1ca62b747a24..93f7ef644f795 100644
--- a/x-pack/plugins/infra/public/lib/lib.ts
+++ b/x-pack/plugins/infra/public/lib/lib.ts
@@ -4,102 +4,18 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { IModule, IScope } from 'angular';
-import { NormalizedCacheObject } from 'apollo-cache-inmemory';
-import ApolloClient from 'apollo-client';
-import { AxiosRequestConfig } from 'axios';
-import React from 'react';
-import { Observable } from 'rxjs';
-import * as rt from 'io-ts';
 import { i18n } from '@kbn/i18n';
-import { SourceQuery } from '../graphql/types';
+import * as rt from 'io-ts';
 import {
-  SnapshotMetricInput,
-  SnapshotGroupBy,
   InfraTimerangeInput,
+  SnapshotGroupBy,
+  SnapshotMetricInput,
   SnapshotNodeMetric,
   SnapshotNodePath,
 } from '../../common/http_api/snapshot_api';
+import { SourceQuery } from '../graphql/types';
 import { WaffleSortOption } from '../pages/metrics/inventory_view/hooks/use_waffle_options';
 
-export interface InfraFrontendLibs {
-  apolloClient: InfraApolloClient;
-  observableApi: InfraObservableApi;
-}
-
-export type InfraTimezoneProvider = () => string;
-
-export type InfraApolloClient = ApolloClient<NormalizedCacheObject>;
-
-export interface InfraFrameworkAdapter {
-  // Insstance vars
-  appState?: object;
-  kbnVersion?: string;
-  timezone?: string;
-
-  // Methods
-  setUISettings(key: string, value: any): void;
-  render(component: React.ReactElement<any>): void;
-  renderBreadcrumbs(component: React.ReactElement<any>): void;
-}
-
-export type InfraFramworkAdapterConstructable = new (
-  uiModule: IModule,
-  timezoneProvider: InfraTimezoneProvider
-) => InfraFrameworkAdapter;
-
-// TODO: replace AxiosRequestConfig with something more defined
-export type InfraRequestConfig = AxiosRequestConfig;
-
-export interface InfraApiAdapter {
-  get<T>(url: string, config?: InfraRequestConfig | undefined): Promise<T>;
-  post(url: string, data?: any, config?: AxiosRequestConfig | undefined): Promise<object>;
-  delete(url: string, config?: InfraRequestConfig | undefined): Promise<object>;
-  put(url: string, data?: any, config?: InfraRequestConfig | undefined): Promise<object>;
-}
-
-export interface InfraObservableApiPostParams<RequestBody extends {} = {}> {
-  url: string;
-  body?: RequestBody;
-}
-
-export type InfraObservableApiResponse<BodyType extends {} = {}> = Observable<{
-  status: number;
-  response: BodyType;
-}>;
-
-export interface InfraObservableApi {
-  post<RequestBody extends {} = {}, ResponseBody extends {} = {}>(
-    params: InfraObservableApiPostParams<RequestBody>
-  ): InfraObservableApiResponse<ResponseBody>;
-}
-
-export interface InfraUiKibanaAdapterScope extends IScope {
-  breadcrumbs: any[];
-  topNavMenu: any[];
-}
-
-export interface InfraKibanaUIConfig {
-  get(key: string): any;
-  set(key: string, value: any): Promise<boolean>;
-}
-
-export interface InfraKibanaAdapterServiceRefs {
-  config: InfraKibanaUIConfig;
-  rootScope: IScope;
-}
-
-export type InfraBufferedKibanaServiceCall<ServiceRefs> = (serviceRefs: ServiceRefs) => void;
-
-export interface InfraField {
-  name: string;
-  type: string;
-  searchable: boolean;
-  aggregatable: boolean;
-}
-
-export type InfraWaffleData = InfraWaffleMapGroup[];
-
 export interface InfraWaffleMapNode {
   pathId: string;
   id: string;
@@ -221,8 +137,6 @@ export interface InfraOptions {
   wafflemap: InfraWaffleMapOptions;
 }
 
-export type Omit<T1, T2> = Pick<T1, Exclude<keyof T1, keyof T2>>;
-
 export interface InfraWaffleMapBounds {
   min: number;
   max: number;
diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx
index 1394fc48107ef..e62b29974674a 100644
--- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx
@@ -35,7 +35,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/?sourceId=default&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
+        to="/stream?sourceId=default&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
       />
     `);
   });
@@ -47,7 +47,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/?sourceId=default&logFilter=(expression:'CONTAINER_FIELD:%20CONTAINER_ID',kind:kuery)"
+        to="/stream?sourceId=default&logFilter=(expression:'CONTAINER_FIELD:%20CONTAINER_ID',kind:kuery)"
       />
     `);
   });
@@ -59,7 +59,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/?sourceId=default&logFilter=(expression:'POD_FIELD:%20POD_ID',kind:kuery)"
+        to="/stream?sourceId=default&logFilter=(expression:'POD_FIELD:%20POD_ID',kind:kuery)"
       />
     `);
   });
@@ -73,7 +73,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/?logPosition=(end:'2019-02-20T14:58:09.404Z',position:(tiebreaker:0,time:1550671089404),start:'2019-02-20T12:58:09.404Z',streamLive:!f)&sourceId=default&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
+        to="/stream?logPosition=(end:'2019-02-20T14:58:09.404Z',position:(tiebreaker:0,time:1550671089404),start:'2019-02-20T12:58:09.404Z',streamLive:!f)&sourceId=default&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
       />
     `);
   });
@@ -89,7 +89,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/?logPosition=(end:'2019-02-20T14:58:09.404Z',position:(tiebreaker:0,time:1550671089404),start:'2019-02-20T12:58:09.404Z',streamLive:!f)&sourceId=default&logFilter=(expression:'(HOST_FIELD:%20HOST_NAME)%20and%20(FILTER_FIELD:FILTER_VALUE)',kind:kuery)"
+        to="/stream?logPosition=(end:'2019-02-20T14:58:09.404Z',position:(tiebreaker:0,time:1550671089404),start:'2019-02-20T12:58:09.404Z',streamLive:!f)&sourceId=default&logFilter=(expression:'(HOST_FIELD:%20HOST_NAME)%20and%20(FILTER_FIELD:FILTER_VALUE)',kind:kuery)"
       />
     `);
   });
@@ -103,7 +103,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/?sourceId=SOME-OTHER-SOURCE&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
+        to="/stream?sourceId=SOME-OTHER-SOURCE&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
       />
     `);
   });
diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx
index d9aaa2da7bbc8..10320ebbe7609 100644
--- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx
@@ -71,7 +71,7 @@ export const RedirectToNodeLogs = ({
     replaceSourceIdInQueryString(sourceId)
   )('');
 
-  return <Redirect to={`/?${searchString}`} />;
+  return <Redirect to={`/stream?${searchString}`} />;
 };
 
 export const getNodeLogsUrl = ({
diff --git a/x-pack/plugins/infra/public/pages/logs/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/page_content.tsx
index 2974939a83215..14c53557ba2c7 100644
--- a/x-pack/plugins/infra/public/pages/logs/page_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/page_content.tsx
@@ -96,6 +96,7 @@ export const LogsPageContent: React.FunctionComponent = () => {
         <Route path={logCategoriesTab.pathname} component={LogEntryCategoriesPage} />
         <Route path={settingsTab.pathname} component={LogsSettingsPage} />
         <RedirectWithQueryParams from={'/analysis'} to={logRateTab.pathname} exact />
+        <RedirectWithQueryParams from={'/'} to={streamTab.pathname} exact />
       </Switch>
     </ColumnarPage>
   );
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/table_view.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/table_view.tsx
index 3b68ad314f7df..764eeb154d346 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/table_view.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/table_view.tsx
@@ -15,7 +15,7 @@ import { fieldToName } from '../lib/field_to_display_name';
 import { NodeContextMenu } from './waffle/node_context_menu';
 import { InventoryItemType } from '../../../../../common/inventory_models/types';
 import { SnapshotNode, SnapshotNodePath } from '../../../../../common/http_api/snapshot_api';
-import { CONTAINER_CLASSNAME } from '../../../../apps/start_app';
+import { CONTAINER_CLASSNAME } from '../../../../apps/common_styles';
 
 interface Props {
   nodes: SnapshotNode[];
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts
index 91cf405dcc759..9a1fbee421294 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts
@@ -26,7 +26,9 @@ export const useWaffleTime = () => {
 
   const [state, setState] = useState<WaffleTimeState>(urlState);
 
-  useEffect(() => setUrlState(state), [setUrlState, state]);
+  useEffect(() => {
+    setUrlState(state);
+  }, [setUrlState, state]);
 
   const { currentTime, isAutoReloading } = urlState;
 
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/metrics_time.test.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/metrics_time.test.tsx
index 17fcc05406470..d2076ad6df502 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/metrics_time.test.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/metrics_time.test.tsx
@@ -4,14 +4,20 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { createMemoryHistory } from 'history';
+import React from 'react';
+import { Router } from 'react-router-dom';
 import { mountHook } from 'test_utils/enzyme_helpers';
-
+import { ScopedHistory } from '../../../../../../../../src/core/public';
 import { useMetricsTime } from './use_metrics_time';
 
 describe('useMetricsTime hook', () => {
   describe('timeRange state', () => {
     it('has a default value', () => {
-      const { getLastHookValue } = mountHook(() => useMetricsTime().timeRange);
+      const { getLastHookValue } = mountHook(
+        () => useMetricsTime().timeRange,
+        createProviderWrapper()
+      );
       const hookValue = getLastHookValue();
       expect(hookValue).toHaveProperty('from');
       expect(hookValue).toHaveProperty('to');
@@ -19,7 +25,7 @@ describe('useMetricsTime hook', () => {
     });
 
     it('can be updated', () => {
-      const { act, getLastHookValue } = mountHook(() => useMetricsTime());
+      const { act, getLastHookValue } = mountHook(() => useMetricsTime(), createProviderWrapper());
 
       const timeRange = {
         from: 'now-15m',
@@ -37,12 +43,15 @@ describe('useMetricsTime hook', () => {
 
   describe('AutoReloading state', () => {
     it('has a default value', () => {
-      const { getLastHookValue } = mountHook(() => useMetricsTime().isAutoReloading);
+      const { getLastHookValue } = mountHook(
+        () => useMetricsTime().isAutoReloading,
+        createProviderWrapper()
+      );
       expect(getLastHookValue()).toBe(false);
     });
 
     it('can be updated', () => {
-      const { act, getLastHookValue } = mountHook(() => useMetricsTime());
+      const { act, getLastHookValue } = mountHook(() => useMetricsTime(), createProviderWrapper());
 
       act(({ setAutoReload }) => {
         setAutoReload(true);
@@ -52,3 +61,17 @@ describe('useMetricsTime hook', () => {
     });
   });
 });
+
+const createProviderWrapper = () => {
+  const INITIAL_URL = '/test-basepath/s/test-space/app/metrics';
+  const history = createMemoryHistory();
+
+  history.push(INITIAL_URL);
+  const scopedHistory = new ScopedHistory(history, INITIAL_URL);
+
+  const ProviderWrapper: React.FC = ({ children }) => {
+    return <Router history={scopedHistory}>{children}</Router>;
+  };
+
+  return ProviderWrapper;
+};
diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts
index deae78e22c6a1..b3765db43335a 100644
--- a/x-pack/plugins/infra/public/plugin.ts
+++ b/x-pack/plugins/infra/public/plugin.ts
@@ -4,54 +4,29 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { i18n } from '@kbn/i18n';
-import { merge } from 'lodash';
 import {
-  Plugin as PluginClass,
+  AppMountParameters,
   CoreSetup,
   CoreStart,
+  Plugin as PluginClass,
   PluginInitializerContext,
-  AppMountParameters,
 } from 'kibana/public';
 import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
+import { createMetricThresholdAlertType } from './alerting/metric_threshold';
+import { getInventoryMetricAlertType } from './components/alerting/inventory/metric_inventory_threshold_alert_type';
+import { getAlertType as getLogsAlertType } from './components/alerting/logs/log_threshold_alert_type';
 import { registerStartSingleton } from './legacy_singletons';
 import { registerFeatures } from './register_feature';
-import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
-import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public';
-import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public';
-import { DataEnhancedSetup, DataEnhancedStart } from '../../data_enhanced/public';
-
-import { TriggersAndActionsUIPublicPluginSetup } from '../../../plugins/triggers_actions_ui/public';
-import { getAlertType as getLogsAlertType } from './components/alerting/logs/log_threshold_alert_type';
-import { getInventoryMetricAlertType } from './components/alerting/inventory/metric_inventory_threshold_alert_type';
-import { createMetricThresholdAlertType } from './alerting/metric_threshold';
+import { ClientPluginsSetup, ClientPluginsStart } from './types';
 
 export type ClientSetup = void;
 export type ClientStart = void;
 
-export interface ClientPluginsSetup {
-  home: HomePublicPluginSetup;
-  data: DataPublicPluginSetup;
-  usageCollection: UsageCollectionSetup;
-  dataEnhanced: DataEnhancedSetup;
-  triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup;
-}
-
-export interface ClientPluginsStart {
-  data: DataPublicPluginStart;
-  dataEnhanced: DataEnhancedStart;
-}
-
-export type InfraPlugins = ClientPluginsSetup & ClientPluginsStart;
-
-const getMergedPlugins = (setup: ClientPluginsSetup, start: ClientPluginsStart): InfraPlugins => {
-  return merge({}, setup, start);
-};
-
 export class Plugin
   implements PluginClass<ClientSetup, ClientStart, ClientPluginsSetup, ClientPluginsStart> {
-  constructor(context: PluginInitializerContext) {}
+  constructor(_context: PluginInitializerContext) {}
 
-  setup(core: CoreSetup, pluginsSetup: ClientPluginsSetup) {
+  setup(core: CoreSetup<ClientPluginsStart, ClientStart>, pluginsSetup: ClientPluginsSetup) {
     registerFeatures(pluginsSetup.home);
 
     pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(getInventoryMetricAlertType());
@@ -69,16 +44,18 @@ export class Plugin
       category: DEFAULT_APP_CATEGORIES.observability,
       mount: async (params: AppMountParameters) => {
         const [coreStart, pluginsStart] = await core.getStartServices();
-        const plugins = getMergedPlugins(pluginsSetup, pluginsStart as ClientPluginsStart);
-        const { startApp, composeLibs, LogsRouter } = await this.downloadAssets();
+        const { renderApp } = await import('./apps/logs_app');
 
-        return startApp(
-          composeLibs(coreStart),
+        return renderApp(
           coreStart,
-          plugins,
-          params,
-          LogsRouter,
-          pluginsSetup.triggers_actions_ui
+          {
+            data: pluginsStart.data,
+            dataEnhanced: pluginsSetup.dataEnhanced,
+            home: pluginsSetup.home,
+            triggers_actions_ui: pluginsStart.triggers_actions_ui,
+            usageCollection: pluginsSetup.usageCollection,
+          },
+          params
         );
       },
     });
@@ -94,16 +71,18 @@ export class Plugin
       category: DEFAULT_APP_CATEGORIES.observability,
       mount: async (params: AppMountParameters) => {
         const [coreStart, pluginsStart] = await core.getStartServices();
-        const plugins = getMergedPlugins(pluginsSetup, pluginsStart as ClientPluginsStart);
-        const { startApp, composeLibs, MetricsRouter } = await this.downloadAssets();
+        const { renderApp } = await import('./apps/metrics_app');
 
-        return startApp(
-          composeLibs(coreStart),
+        return renderApp(
           coreStart,
-          plugins,
-          params,
-          MetricsRouter,
-          pluginsSetup.triggers_actions_ui
+          {
+            data: pluginsStart.data,
+            dataEnhanced: pluginsSetup.dataEnhanced,
+            home: pluginsSetup.home,
+            triggers_actions_ui: pluginsStart.triggers_actions_ui,
+            usageCollection: pluginsSetup.usageCollection,
+          },
+          params
         );
       },
     });
@@ -116,28 +95,14 @@ export class Plugin
       title: 'infra',
       navLinkStatus: 3,
       mount: async (params: AppMountParameters) => {
-        const { startLegacyApp } = await import('./apps/start_legacy_app');
-        return startLegacyApp(params);
+        const { renderApp } = await import('./apps/legacy_app');
+
+        return renderApp(params);
       },
     });
   }
 
-  start(core: CoreStart, plugins: ClientPluginsStart) {
+  start(core: CoreStart, _plugins: ClientPluginsStart) {
     registerStartSingleton(core);
   }
-
-  private async downloadAssets() {
-    const [{ startApp }, { composeLibs }, { LogsRouter, MetricsRouter }] = await Promise.all([
-      import('./apps/start_app'),
-      import('./compose_libs'),
-      import('./routers'),
-    ]);
-
-    return {
-      startApp,
-      composeLibs,
-      LogsRouter,
-      MetricsRouter,
-    };
-  }
 }
diff --git a/x-pack/plugins/infra/public/routers/index.ts b/x-pack/plugins/infra/public/routers/index.ts
deleted file mode 100644
index 71ab2613d8dc1..0000000000000
--- a/x-pack/plugins/infra/public/routers/index.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import { History } from 'history';
-
-export * from './logs_router';
-export * from './metrics_router';
-
-interface RouterProps {
-  history: History;
-}
-
-export type AppRouter = React.FC<RouterProps>;
diff --git a/x-pack/plugins/infra/public/routers/logs_router.tsx b/x-pack/plugins/infra/public/routers/logs_router.tsx
deleted file mode 100644
index 8258f087b5872..0000000000000
--- a/x-pack/plugins/infra/public/routers/logs_router.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { Route, Router, Switch } from 'react-router-dom';
-
-import { NotFoundPage } from '../pages/404';
-import { LinkToLogsPage } from '../pages/link_to';
-import { LogsPage } from '../pages/logs';
-import { RedirectWithQueryParams } from '../utils/redirect_with_query_params';
-import { useKibana } from '../../../../../src/plugins/kibana_react/public';
-import { AppRouter } from './index';
-
-export const LogsRouter: AppRouter = ({ history }) => {
-  const uiCapabilities = useKibana().services.application?.capabilities;
-  return (
-    <Router history={history}>
-      <Switch>
-        <Route path="/link-to" component={LinkToLogsPage} />
-        {uiCapabilities?.logs?.show && (
-          <RedirectWithQueryParams from="/" exact={true} to="/stream" />
-        )}
-        {uiCapabilities?.logs?.show && <Route path="/" component={LogsPage} />}
-        <Route component={NotFoundPage} />
-      </Switch>
-    </Router>
-  );
-};
diff --git a/x-pack/plugins/infra/public/routers/metrics_router.tsx b/x-pack/plugins/infra/public/routers/metrics_router.tsx
deleted file mode 100644
index 0e427150a46cc..0000000000000
--- a/x-pack/plugins/infra/public/routers/metrics_router.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { Route, Router, Switch } from 'react-router-dom';
-
-import { NotFoundPage } from '../pages/404';
-import { InfrastructurePage } from '../pages/metrics';
-import { MetricDetail } from '../pages/metrics/metric_detail';
-import { RedirectWithQueryParams } from '../utils/redirect_with_query_params';
-import { useKibana } from '../../../../../src/plugins/kibana_react/public';
-import { AppRouter } from './index';
-import { LinkToMetricsPage } from '../pages/link_to';
-
-export const MetricsRouter: AppRouter = ({ history }) => {
-  const uiCapabilities = useKibana().services.application?.capabilities;
-  return (
-    <Router history={history}>
-      <Switch>
-        <Route path="/link-to" component={LinkToMetricsPage} />
-        {uiCapabilities?.infrastructure?.show && (
-          <RedirectWithQueryParams from="/" exact={true} to="/inventory" />
-        )}
-        {uiCapabilities?.infrastructure?.show && (
-          <RedirectWithQueryParams from="/snapshot" exact={true} to="/inventory" />
-        )}
-        {uiCapabilities?.infrastructure?.show && (
-          <RedirectWithQueryParams from="/metrics-explorer" exact={true} to="/explorer" />
-        )}
-        {uiCapabilities?.infrastructure?.show && (
-          <Route path="/detail/:type/:node" component={MetricDetail} />
-        )}
-        {uiCapabilities?.infrastructure?.show && <Route path="/" component={InfrastructurePage} />}
-        <Route component={NotFoundPage} />
-      </Switch>
-    </Router>
-  );
-};
diff --git a/x-pack/plugins/infra/public/types.ts b/x-pack/plugins/infra/public/types.ts
new file mode 100644
index 0000000000000..8181da3301c92
--- /dev/null
+++ b/x-pack/plugins/infra/public/types.ts
@@ -0,0 +1,25 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { DataPublicPluginStart } from '../../../../src/plugins/data/public';
+import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
+import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public';
+import { TriggersAndActionsUIPublicPluginSetup } from '../../../plugins/triggers_actions_ui/public';
+import { DataEnhancedSetup } from '../../data_enhanced/public';
+
+export interface ClientPluginsSetup {
+  dataEnhanced: DataEnhancedSetup;
+  home: HomePublicPluginSetup;
+  triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup;
+  usageCollection: UsageCollectionSetup;
+}
+
+export interface ClientPluginsStart {
+  data: DataPublicPluginStart;
+  triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup;
+}
+
+export type ClientPluginDeps = ClientPluginsSetup & ClientPluginsStart;
diff --git a/x-pack/plugins/infra/public/utils/apollo_client.ts b/x-pack/plugins/infra/public/utils/apollo_client.ts
new file mode 100644
index 0000000000000..3c69ef4c98fac
--- /dev/null
+++ b/x-pack/plugins/infra/public/utils/apollo_client.ts
@@ -0,0 +1,85 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
+import ApolloClient from 'apollo-client';
+import { ApolloLink } from 'apollo-link';
+import { createHttpLink } from 'apollo-link-http';
+import { withClientState } from 'apollo-link-state';
+import { HttpFetchOptions, HttpHandler } from 'src/core/public';
+import introspectionQueryResultData from '../graphql/introspection.json';
+
+export const createApolloClient = (fetch: HttpHandler) => {
+  const cache = new InMemoryCache({
+    addTypename: false,
+    fragmentMatcher: new IntrospectionFragmentMatcher({
+      introspectionQueryResultData,
+    }),
+  });
+
+  const wrappedFetch = (path: string, options: HttpFetchOptions) => {
+    return new Promise<Response>(async (resolve, reject) => {
+      // core.http.fetch isn't 100% compatible with the Fetch API and will
+      // throw Errors on 401s. This top level try / catch handles those scenarios.
+      try {
+        fetch(path, {
+          ...options,
+          // Set headers to undefined due to this bug: https://github.com/apollographql/apollo-link/issues/249,
+          // Apollo will try to set a "content-type" header which will conflict with the "Content-Type" header that
+          // core.http.fetch correctly sets.
+          headers: undefined,
+          asResponse: true,
+        }).then((res) => {
+          if (!res.response) {
+            return reject();
+          }
+          // core.http.fetch will parse the Response and set a body before handing it back. As such .text() / .json()
+          // will have already been called on the Response instance. However, Apollo will also want to call
+          // .text() / .json() on the instance, as it expects the raw Response instance, rather than core's wrapper.
+          // .text() / .json() can only be called once, and an Error will be thrown if those methods are accessed again.
+          // This hacks around that by setting up a new .text() method that will restringify the JSON response we already have.
+          // This does result in an extra stringify / parse cycle, which isn't ideal, but as we only have a few endpoints left using
+          // GraphQL this shouldn't create excessive overhead.
+          // Ref: https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-http/src/httpLink.ts#L134
+          // and
+          // https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-http-common/src/index.ts#L125
+          return resolve({
+            ...res.response,
+            text: () => {
+              return new Promise(async (resolveText, rejectText) => {
+                if (res.body) {
+                  return resolveText(JSON.stringify(res.body));
+                } else {
+                  return rejectText();
+                }
+              });
+            },
+          });
+        });
+      } catch (error) {
+        reject(error);
+      }
+    });
+  };
+
+  const HttpLink = createHttpLink({
+    fetch: wrappedFetch,
+    uri: `/api/infra/graphql`,
+  });
+
+  const graphQLOptions = {
+    cache,
+    link: ApolloLink.from([
+      withClientState({
+        cache,
+        resolvers: {},
+      }),
+      HttpLink,
+    ]),
+  };
+
+  return new ApolloClient(graphQLOptions);
+};
diff --git a/x-pack/plugins/infra/public/utils/triggers_actions_context.tsx b/x-pack/plugins/infra/public/utils/triggers_actions_context.tsx
index 1cff3663280fd..6b51714893a6d 100644
--- a/x-pack/plugins/infra/public/utils/triggers_actions_context.tsx
+++ b/x-pack/plugins/infra/public/utils/triggers_actions_context.tsx
@@ -5,10 +5,10 @@
  */
 
 import * as React from 'react';
-import { TriggersAndActionsUIPublicPluginSetup } from '../../../triggers_actions_ui/public';
+import { TriggersAndActionsUIPublicPluginStart } from '../../../triggers_actions_ui/public';
 
 interface ContextProps {
-  triggersActionsUI: TriggersAndActionsUIPublicPluginSetup | null;
+  triggersActionsUI: TriggersAndActionsUIPublicPluginStart | null;
 }
 
 export const TriggerActionsContext = React.createContext<ContextProps>({
@@ -16,7 +16,7 @@ export const TriggerActionsContext = React.createContext<ContextProps>({
 });
 
 interface Props {
-  triggersActionsUI: TriggersAndActionsUIPublicPluginSetup;
+  triggersActionsUI: TriggersAndActionsUIPublicPluginStart;
 }
 
 export const TriggersActionsProvider: React.FC<Props> = (props) => {
diff --git a/x-pack/plugins/infra/public/utils/use_url_state.ts b/x-pack/plugins/infra/public/utils/use_url_state.ts
index 7a63b48fa9a1a..ab0ca1311194f 100644
--- a/x-pack/plugins/infra/public/utils/use_url_state.ts
+++ b/x-pack/plugins/infra/public/utils/use_url_state.ts
@@ -8,10 +8,9 @@ import { parse, stringify } from 'query-string';
 import { Location } from 'history';
 import { useCallback, useEffect, useMemo, useState } from 'react';
 import { decode, encode, RisonValue } from 'rison-node';
+import { useHistory } from 'react-router-dom';
 import { url } from '../../../../../src/plugins/kibana_utils/public';
 
-import { useHistory } from './history_context';
-
 export const useUrlState = <State>({
   defaultState,
   decodeUrlState,