diff --git a/src/plugins/dashboard/public/application/components/dashboard_editor.tsx b/src/plugins/dashboard/public/application/components/dashboard_editor.tsx
index d3d0814f0c4a..2be15917e940 100644
--- a/src/plugins/dashboard/public/application/components/dashboard_editor.tsx
+++ b/src/plugins/dashboard/public/application/components/dashboard_editor.tsx
@@ -3,8 +3,212 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import React from 'react';
+import React, { useEffect, useState } from 'react';
+import { useParams } from 'react-router-dom';
+import { EventEmitter } from 'events';
+import { EMPTY, Subscription, merge } from 'rxjs';
+import { catchError, distinctUntilChanged, map, mapTo, startWith, switchMap } from 'rxjs/operators';
+import deepEqual from 'fast-deep-equal';
+import { DashboardTopNav } from '../components/dashboard_top_nav';
+import { useChromeVisibility } from '../utils/use/use_chrome_visibility';
+import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
+import { useSavedDashboardInstance } from '../utils/use/use_saved_dashboard_instance';
+
+import { DashboardServices, SavedDashboardPanel } from '../../types';
+import {
+  DASHBOARD_CONTAINER_TYPE,
+  DashboardContainer,
+  DashboardContainerInput,
+  DashboardPanelState,
+} from '../embeddable';
+import {
+  ContainerOutput,
+  ErrorEmbeddable,
+  ViewMode,
+  isErrorEmbeddable,
+} from '../../embeddable_plugin';
+import { DashboardEmptyScreen, DashboardEmptyScreenProps } from '../dashboard_empty_screen';
+import { convertSavedDashboardPanelToPanelState } from '../lib/embeddable_saved_object_converters';
+import { useDashboardAppState } from '../utils/use/use_dashboard_app_state';
 
 export const DashboardEditor = () => {
-  return <div>Dashboard Editor</div>;
+  const { id: dashboardIdFromUrl } = useParams<{ id: string }>();
+  const { services } = useOpenSearchDashboards<DashboardServices>();
+  const { embeddable, data, dashboardConfig, embeddableCapabilities, uiSettings, http } = services;
+  const { query: queryService } = data;
+  const { visualizeCapabilities, mapsCapabilities } = embeddableCapabilities;
+  const timefilter = queryService.timefilter.timefilter;
+  const isChromeVisible = useChromeVisibility(services.chrome);
+  const [eventEmitter] = useState(new EventEmitter());
+
+  const { savedDashboardInstance } = useSavedDashboardInstance(
+    services,
+    eventEmitter,
+    isChromeVisible,
+    dashboardIdFromUrl
+  );
+
+  const { appState } = useDashboardAppState(services, eventEmitter, savedDashboardInstance);
+
+  const appStateData = appState?.get();
+  if (!appStateData) {
+    return null;
+  }
+  let dashboardContainer: DashboardContainer | undefined;
+  let inputSubscription: Subscription | undefined;
+  let outputSubscription: Subscription | undefined;
+
+  const dashboardDom = document.getElementById('dashboardViewport');
+  const dashboardFactory = embeddable.getEmbeddableFactory<
+    DashboardContainerInput,
+    ContainerOutput,
+    DashboardContainer
+  >(DASHBOARD_CONTAINER_TYPE);
+
+  const getShouldShowEditHelp = () => {
+    return (
+      !appStateData.panels.length &&
+      appStateData.viewMode === ViewMode.EDIT &&
+      !dashboardConfig.getHideWriteControls()
+    );
+  };
+
+  const getShouldShowViewHelp = () => {
+    return (
+      !appStateData.panels.length &&
+      appStateData.viewMode === ViewMode.VIEW &&
+      !dashboardConfig.getHideWriteControls()
+    );
+  };
+
+  const shouldShowUnauthorizedEmptyState = () => {
+    const readonlyMode =
+      !appStateData.panels.length &&
+      !getShouldShowEditHelp() &&
+      !getShouldShowViewHelp() &&
+      dashboardConfig.getHideWriteControls();
+    const userHasNoPermissions =
+      !appStateData.panels.length && !visualizeCapabilities.save && !mapsCapabilities.save;
+    return readonlyMode || userHasNoPermissions;
+  };
+
+  const getEmptyScreenProps = (
+    shouldShowEditHelp: boolean,
+    isEmptyInReadOnlyMode: boolean
+  ): DashboardEmptyScreenProps => {
+    const emptyScreenProps: DashboardEmptyScreenProps = {
+      onLinkClick: () => {}, // TODO
+      showLinkToVisualize: shouldShowEditHelp,
+      uiSettings,
+      http,
+    };
+    if (shouldShowEditHelp) {
+      emptyScreenProps.onVisualizeClick = () => {
+        alert('click'); // TODO
+      };
+    }
+    if (isEmptyInReadOnlyMode) {
+      emptyScreenProps.isReadonlyMode = true;
+    }
+    return emptyScreenProps;
+  };
+
+  const getDashboardInput = () => {
+    const embeddablesMap: {
+      [key: string]: DashboardPanelState;
+    } = {};
+    appStateData.panels.forEach((panel: SavedDashboardPanel) => {
+      embeddablesMap[panel.panelIndex] = convertSavedDashboardPanelToPanelState(panel);
+    });
+
+    const lastReloadRequestTime = 0;
+    return {
+      id: savedDashboardInstance.id || '',
+      filters: appStateData.filters,
+      hidePanelTitles: appStateData?.options.hidePanelTitles,
+      query: appStateData.query,
+      timeRange: {
+        ..._.cloneDeep(timefilter.getTime()),
+      },
+      refreshConfig: timefilter.getRefreshInterval(),
+      viewMode: appStateData.viewMode,
+      panels: embeddablesMap,
+      isFullScreenMode: appStateData?.fullScreenMode,
+      isEmbeddedExternally: false, // TODO
+      // isEmptyState: shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadonlyMode,
+      isEmptyState: false, // TODO
+      useMargins: appStateData.options.useMargins,
+      lastReloadRequestTime, // TODO
+      title: appStateData.title,
+      description: appStateData.description,
+      expandedPanelId: appStateData.expandedPanelId,
+    };
+  };
+
+  if (dashboardFactory) {
+    dashboardFactory
+      .create(getDashboardInput())
+      .then((container: DashboardContainer | ErrorEmbeddable | undefined) => {
+        if (container && !isErrorEmbeddable(container)) {
+          dashboardContainer = container;
+
+          dashboardContainer.renderEmpty = () => {
+            const shouldShowEditHelp = getShouldShowEditHelp();
+            const shouldShowViewHelp = getShouldShowViewHelp();
+            const isEmptyInReadOnlyMode = shouldShowUnauthorizedEmptyState();
+            const isEmptyState = shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadOnlyMode;
+            return isEmptyState ? (
+              <DashboardEmptyScreen
+                {...getEmptyScreenProps(shouldShowEditHelp, isEmptyInReadOnlyMode)}
+              />
+            ) : null;
+          };
+
+          outputSubscription = merge(
+            // output of dashboard container itself
+            dashboardContainer.getOutput$(),
+            // plus output of dashboard container children,
+            // children may change, so make sure we subscribe/unsubscribe with switchMap
+            dashboardContainer.getOutput$().pipe(
+              map(() => dashboardContainer!.getChildIds()),
+              distinctUntilChanged(deepEqual),
+              switchMap((newChildIds: string[]) =>
+                merge(
+                  ...newChildIds.map((childId) =>
+                    dashboardContainer!
+                      .getChild(childId)
+                      .getOutput$()
+                      .pipe(catchError(() => EMPTY))
+                  )
+                )
+              )
+            )
+          )
+            .pipe(
+              mapTo(dashboardContainer),
+              startWith(dashboardContainer) // to trigger initial index pattern update
+              // updateIndexPatternsOperator //TODO
+            )
+            .subscribe();
+
+          inputSubscription = dashboardContainer.getInput$().subscribe(() => {});
+
+          if (dashboardDom && container) {
+            container.render(dashboardDom);
+          }
+        }
+      });
+  }
+
+  return (
+    <div>
+      {savedDashboardInstance && appState && (
+        <DashboardTopNav
+          isChromeVisible={isChromeVisible}
+          savedDashboardInstance={savedDashboardInstance}
+          appState={appState}
+        />
+      )}
+    </div>
+  );
 };
diff --git a/src/plugins/dashboard/public/application/components/dashboard_top_nav.tsx b/src/plugins/dashboard/public/application/components/dashboard_top_nav.tsx
index 1603fb89dad5..4ee92567c590 100644
--- a/src/plugins/dashboard/public/application/components/dashboard_top_nav.tsx
+++ b/src/plugins/dashboard/public/application/components/dashboard_top_nav.tsx
@@ -3,8 +3,115 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import React from 'react';
+import React, { memo, useState, useEffect } from 'react';
+import { Filter } from 'src/plugins/data/public';
+import { useCallback } from 'react';
+import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
+import { getTopNavConfig } from '../top_nav/get_top_nav_config';
+import { DashboardAppState, DashboardServices, NavAction } from '../../types';
 
-export const DashboardTopNav = () => {
-  return <div>Dashboard Top Nav</div>;
+interface DashboardTopNavProps {
+  isChromeVisible: boolean;
+  savedDashboardInstance: any;
+  appState: DashboardAppState;
+}
+
+enum UrlParams {
+  SHOW_TOP_MENU = 'show-top-menu',
+  SHOW_QUERY_INPUT = 'show-query-input',
+  SHOW_TIME_FILTER = 'show-time-filter',
+  SHOW_FILTER_BAR = 'show-filter-bar',
+  HIDE_FILTER_BAR = 'hide-filter-bar',
+}
+
+const TopNav = ({ isChromeVisible, savedDashboardInstance, appState }: DashboardTopNavProps) => {
+  const [filters, setFilters] = useState<Filter[]>([]);
+  const [topNavMenu, setTopNavMenu] = useState<any>();
+  const [isFullScreenMode, setIsFullScreenMode] = useState<any>();
+
+  const { services } = useOpenSearchDashboards<DashboardServices>();
+  const { TopNavMenu } = services.navigation.ui;
+  const { data, dashboardConfig, setHeaderActionMenu } = services;
+  const { query: queryService } = data;
+
+  // TODO: this should base on URL
+  const isEmbeddedExternally = false;
+
+  // TODO: should use URL params
+  const shouldForceDisplay = (param: string): boolean => {
+    // const [searchParams] = useSearchParams();
+    return false;
+  };
+
+  const shouldShowNavBarComponent = (forceShow: boolean): boolean =>
+    (forceShow || isChromeVisible) && !appState?.fullScreenMode;
+
+  useEffect(() => {
+    setFilters(queryService.filterManager.getFilters());
+  }, [services, queryService]);
+
+  useEffect(() => {
+    const navActions: {
+      [key: string]: NavAction;
+    } = {}; // TODO: need to implement nav actions
+    setTopNavMenu(
+      getTopNavConfig(appState?.viewMode, navActions, dashboardConfig.getHideWriteControls())
+    );
+  }, [appState, services, dashboardConfig]);
+
+  useEffect(() => {
+    setIsFullScreenMode(appState?.fullScreenMode);
+  }, [appState, services]);
+
+  const shouldShowFilterBar = (forceHide: boolean): boolean =>
+    !forceHide && (filters!.length > 0 || !appState?.fullScreenMode);
+
+  const forceShowTopNavMenu = shouldForceDisplay(UrlParams.SHOW_TOP_MENU);
+  const forceShowQueryInput = shouldForceDisplay(UrlParams.SHOW_QUERY_INPUT);
+  const forceShowDatePicker = shouldForceDisplay(UrlParams.SHOW_TIME_FILTER);
+  const forceHideFilterBar = shouldForceDisplay(UrlParams.HIDE_FILTER_BAR);
+  const showTopNavMenu = shouldShowNavBarComponent(forceShowTopNavMenu);
+  const showQueryInput = shouldShowNavBarComponent(forceShowQueryInput);
+  const showDatePicker = shouldShowNavBarComponent(forceShowDatePicker);
+  const showQueryBar = showQueryInput || showDatePicker;
+  const showFilterBar = shouldShowFilterBar(forceHideFilterBar);
+  const showSearchBar = showQueryBar || showFilterBar;
+
+  // TODO: implement handleRefresh
+  const handleRefresh = useCallback((_payload: any, isUpdate?: boolean) => {
+    /* if (isUpdate === false) {
+        // The user can still request a reload in the query bar, even if the
+        // query is the same, and in that case, we have to explicitly ask for
+        // a reload, since no state changes will cause it.
+        lastReloadRequestTime = new Date().getTime();
+        const changes = getChangesFromAppStateForContainerState();
+    if (changes && dashboardContainer) {
+      dashboardContainer.updateInput(changes);
+    }*/
+  }, []);
+
+  return isChromeVisible ? (
+    <TopNavMenu
+      appName={'dashboard'}
+      savedQueryId={appState?.savedQuery}
+      config={showTopNavMenu ? topNavMenu : undefined}
+      className={isFullScreenMode ? 'osdTopNavMenu-isFullScreen' : undefined}
+      screenTitle={appState.title}
+      // showTopNavMenu={showTopNavMenu}
+      showSearchBar={showSearchBar}
+      showQueryBar={showQueryBar}
+      showQueryInput={showQueryInput}
+      showDatePicker={showDatePicker}
+      showFilterBar={showFilterBar}
+      useDefaultBehaviors={true}
+      indexPatterns={[]}
+      showSaveQuery={services.dashboardCapabilities.saveQuery as boolean}
+      savedQuery={undefined}
+      onSavedQueryIdChange={() => {}}
+      onQuerySubmit={handleRefresh}
+      setMenuMountPoint={isEmbeddedExternally ? undefined : setHeaderActionMenu}
+    />
+  ) : null;
 };
+
+export const DashboardTopNav = memo(TopNav);
diff --git a/src/plugins/dashboard/public/application/utils/create_dashboard_app_state.tsx b/src/plugins/dashboard/public/application/utils/create_dashboard_app_state.tsx
new file mode 100644
index 000000000000..e4c0d9448a2d
--- /dev/null
+++ b/src/plugins/dashboard/public/application/utils/create_dashboard_app_state.tsx
@@ -0,0 +1,117 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { migrateAppState } from '../lib/migrate_app_state';
+import {
+  IOsdUrlStateStorage,
+  createStateContainer,
+  syncState,
+} from '../../../../opensearch_dashboards_utils/public';
+import {
+  DashboardAppState,
+  DashboardAppStateTransitions,
+  DashboardAppStateInUrl,
+  DashboardServices,
+} from '../../types';
+import { ViewMode } from '../../embeddable_plugin';
+import { getDashboardIdFromUrl } from '../lib';
+
+const STATE_STORAGE_KEY = '_a';
+
+interface Arguments {
+  osdUrlStateStorage: IOsdUrlStateStorage;
+  stateDefaults: DashboardAppState;
+  services: DashboardServices;
+  instance: any;
+}
+
+export const createDashboardAppState = ({
+  stateDefaults,
+  osdUrlStateStorage,
+  services,
+  instance,
+}: Arguments) => {
+  const urlState = osdUrlStateStorage.get<DashboardAppState>(STATE_STORAGE_KEY);
+  const { opensearchDashboardsVersion, usageCollection, history } = services;
+  const initialState = migrateAppState(
+    {
+      ...stateDefaults,
+      ...urlState,
+    },
+    opensearchDashboardsVersion,
+    usageCollection
+  );
+
+  const pureTransitions = {
+    set: (state) => (prop, value) => ({ ...state, [prop]: value }),
+    setOption: (state) => (option, value) => ({
+      ...state,
+      options: {
+        ...state.options,
+        [option]: value,
+      },
+    }),
+  } as DashboardAppStateTransitions;
+  /*
+     make sure url ('_a') matches initial state
+     Initializing appState does two things - first it translates the defaults into AppState,
+     second it updates appState based on the url (the url trumps the defaults). This means if
+     we update the state format at all and want to handle BWC, we must not only migrate the
+     data stored with saved vis, but also any old state in the url.
+   */
+  osdUrlStateStorage.set(STATE_STORAGE_KEY, initialState, { replace: true });
+
+  const stateContainer = createStateContainer<DashboardAppState, DashboardAppStateTransitions>(
+    initialState,
+    pureTransitions
+  );
+
+  const toUrlState = (state: DashboardAppState): DashboardAppStateInUrl => {
+    if (state.viewMode === ViewMode.VIEW) {
+      const { panels, ...stateWithoutPanels } = state;
+      return stateWithoutPanels;
+    }
+    return state;
+  };
+
+  const { start: startStateSync, stop: stopStateSync } = syncState({
+    storageKey: STATE_STORAGE_KEY,
+    stateContainer: {
+      ...stateContainer,
+      get: () => toUrlState(stateContainer.get()),
+      set: (state: DashboardAppStateInUrl | null) => {
+        // sync state required state container to be able to handle null
+        // overriding set() so it could handle null coming from url
+        if (state) {
+          // Skip this update if current dashboardId in the url is different from what we have in the current instance of state manager
+          // As dashboard is driven by angular at the moment, the destroy cycle happens async,
+          // If the dashboardId has changed it means this instance
+          // is going to be destroyed soon and we shouldn't sync state anymore,
+          // as it could potentially trigger further url updates
+          const currentDashboardIdInUrl = getDashboardIdFromUrl(history.location.pathname);
+          if (currentDashboardIdInUrl !== instance.id) return;
+
+          stateContainer.set({
+            ...stateDefaults,
+            ...state,
+          });
+        } else {
+          // Do nothing in case when state from url is empty,
+          // this fixes: https://github.com/elastic/kibana/issues/57789
+          // There are not much cases when state in url could become empty:
+          // 1. User manually removed `_a` from the url
+          // 2. Browser is navigating away from the page and most likely there is no `_a` in the url.
+          //    In this case we don't want to do any state updates
+          //    and just allow $scope.$on('destroy') fire later and clean up everything
+        }
+      },
+    },
+    stateStorage: osdUrlStateStorage,
+  });
+
+  // start syncing the appState with the ('_a') url
+  startStateSync();
+  return { stateContainer, stopStateSync };
+};
diff --git a/src/plugins/dashboard/public/application/utils/use/use_chrome_visibility.ts b/src/plugins/dashboard/public/application/utils/use/use_chrome_visibility.ts
new file mode 100644
index 000000000000..7abb5a6d355a
--- /dev/null
+++ b/src/plugins/dashboard/public/application/utils/use/use_chrome_visibility.ts
@@ -0,0 +1,27 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ *
+ * Any modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
+ */
+
+import { useState, useEffect } from 'react';
+import { ChromeStart } from 'opensearch-dashboards/public';
+
+export const useChromeVisibility = (chrome: ChromeStart) => {
+  const [isVisible, setIsVisible] = useState<boolean>(true);
+
+  useEffect(() => {
+    const subscription = chrome.getIsVisible$().subscribe((value: boolean) => {
+      setIsVisible(value);
+    });
+
+    return () => subscription.unsubscribe();
+  }, [chrome]);
+
+  return isVisible;
+};
diff --git a/src/plugins/dashboard/public/application/utils/use/use_dashboard_app_state.tsx b/src/plugins/dashboard/public/application/utils/use/use_dashboard_app_state.tsx
new file mode 100644
index 000000000000..e14e790125f0
--- /dev/null
+++ b/src/plugins/dashboard/public/application/utils/use/use_dashboard_app_state.tsx
@@ -0,0 +1,88 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import EventEmitter from 'events';
+import { useEffect, useState } from 'react';
+import { cloneDeep } from 'lodash';
+import { map } from 'rxjs/operators';
+import { connectToQueryState, opensearchFilters } from '../../../../../data/public';
+import { migrateLegacyQuery } from '../../lib/migrate_legacy_query';
+import { DashboardServices } from '../../../types';
+
+import { DashboardAppStateContainer } from '../../../types';
+import { migrateAppState, getAppStateDefaults } from '../../lib';
+import { createDashboardAppState } from '../create_dashboard_app_state';
+
+/**
+ * This effect is responsible for instantiating the dashboard app state container,
+ * which is in sync with "_a" url param
+ */
+export const useDashboardAppState = (
+  services: DashboardServices,
+  eventEmitter: EventEmitter,
+  instance: any
+) => {
+  const [appState, setAppState] = useState<DashboardAppStateContainer | null>(null);
+
+  useEffect(() => {
+    if (!instance) {
+      return;
+    }
+    const { dashboardConfig, usageCollection, opensearchDashboardsVersion } = services;
+    const hideWriteControls = dashboardConfig.getHideWriteControls();
+    const stateDefaults = migrateAppState(
+      getAppStateDefaults(instance, hideWriteControls),
+      opensearchDashboardsVersion,
+      usageCollection
+    );
+
+    const { stateContainer, stopStateSync } = createDashboardAppState({
+      stateDefaults,
+      osdUrlStateStorage: services.osdUrlStateStorage,
+      services,
+      instance,
+    });
+
+    const { filterManager, queryString } = services.data.query;
+
+    // sync initial app state from state container to managers
+    filterManager.setAppFilters(cloneDeep(stateContainer.getState().filters));
+    queryString.setQuery(migrateLegacyQuery(stateContainer.getState().query));
+
+    // setup syncing of app filters between app state and query services
+    const stopSyncingAppFilters = connectToQueryState(
+      services.data.query,
+      {
+        set: ({ filters, query }) => {
+          stateContainer.transitions.set('filters', filters || []);
+          stateContainer.transitions.set('query', query || queryString.getDefaultQuery());
+        },
+        get: () => ({
+          filters: stateContainer.getState().filters,
+          query: migrateLegacyQuery(stateContainer.getState().query),
+        }),
+        state$: stateContainer.state$.pipe(
+          map((state) => ({
+            filters: state.filters,
+            query: queryString.formatQuery(state.query),
+          }))
+        ),
+      },
+      {
+        filters: opensearchFilters.FilterStateStore.APP_STATE,
+        query: true,
+      }
+    );
+
+    setAppState(stateContainer);
+
+    return () => {
+      stopStateSync();
+      stopSyncingAppFilters();
+    };
+  }, [eventEmitter, instance, services]);
+
+  return { appState };
+};
diff --git a/src/plugins/dashboard/public/application/utils/use/use_saved_dashboard_instance.ts b/src/plugins/dashboard/public/application/utils/use/use_saved_dashboard_instance.ts
new file mode 100644
index 000000000000..e7e1633ac41b
--- /dev/null
+++ b/src/plugins/dashboard/public/application/utils/use/use_saved_dashboard_instance.ts
@@ -0,0 +1,120 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { i18n } from '@osd/i18n';
+import { EventEmitter } from 'events';
+import { useEffect, useRef, useState } from 'react';
+import {
+  redirectWhenMissing,
+  SavedObjectNotFound,
+} from '../../../../../opensearch_dashboards_utils/public';
+import { DashboardConstants } from '../../../dashboard_constants';
+import { DashboardServices } from '../../../types';
+
+/**
+ * This effect is responsible for instantiating a saved dashboard or creating a new one
+ * using url parameters, embedding and destroying it in DOM
+ */
+export const useSavedDashboardInstance = (
+  services: DashboardServices,
+  eventEmitter: EventEmitter,
+  isChromeVisible: boolean | undefined,
+  dashboardIdFromUrl: string | undefined
+) => {
+  const [state, setState] = useState<{
+    savedDashboardInstance?: any;
+  }>({});
+
+  const dashboardId = useRef('');
+
+  useEffect(() => {
+    const getSavedDashboardInstance = async () => {
+      const {
+        application: { navigateToApp },
+        chrome,
+        history,
+        http: { basePath },
+        notifications,
+        savedDashboards,
+      } = services;
+      try {
+        console.log('trying to get saved dashboard');
+        let savedDashboardInstance: any;
+        if (history.location.pathname === '/create') {
+          try {
+            savedDashboardInstance = await savedDashboards.get();
+          } catch {
+            redirectWhenMissing({
+              history,
+              basePath,
+              navigateToApp,
+              mapping: {
+                dashboard: DashboardConstants.LANDING_PAGE_PATH,
+              },
+              toastNotifications: notifications.toasts,
+            });
+          }
+        } else if (dashboardIdFromUrl) {
+          try {
+            savedDashboardInstance = await savedDashboards.get(dashboardIdFromUrl);
+            chrome.recentlyAccessed.add(
+              savedDashboardInstance.getFullPath(),
+              savedDashboardInstance.title,
+              dashboardIdFromUrl
+            );
+            console.log('saved dashboard', savedDashboardInstance);
+          } catch (error) {
+            // Preserve BWC of v5.3.0 links for new, unsaved dashboards.
+            // See https://github.com/elastic/kibana/issues/10951 for more context.
+            if (error instanceof SavedObjectNotFound && dashboardIdFromUrl === 'create') {
+              // Note preserve querystring part is necessary so the state is preserved through the redirect.
+              history.replace({
+                ...history.location, // preserve query,
+                pathname: DashboardConstants.CREATE_NEW_DASHBOARD_URL,
+              });
+
+              notifications.toasts.addWarning(
+                i18n.translate('dashboard.urlWasRemovedInSixZeroWarningMessage', {
+                  defaultMessage:
+                    'The url "dashboard/create" was removed in 6.0. Please update your bookmarks.',
+                })
+              );
+              return new Promise(() => {});
+            } else {
+              // E.g. a corrupt or deleted dashboard
+              notifications.toasts.addDanger(error.message);
+              history.push(DashboardConstants.LANDING_PAGE_PATH);
+              return new Promise(() => {});
+            }
+          }
+        }
+
+        setState({ savedDashboardInstance });
+      } catch (error) {}
+    };
+
+    if (isChromeVisible === undefined) {
+      // waiting for specifying chrome
+      return;
+    }
+
+    if (!dashboardId.current) {
+      dashboardId.current = dashboardIdFromUrl || 'new';
+      getSavedDashboardInstance();
+    } else if (
+      dashboardIdFromUrl &&
+      dashboardId.current !== dashboardIdFromUrl &&
+      state.savedDashboardInstance?.id !== dashboardIdFromUrl
+    ) {
+      dashboardId.current = dashboardIdFromUrl;
+      setState({});
+      getSavedDashboardInstance();
+    }
+  }, [eventEmitter, isChromeVisible, services, state.savedDashboardInstance, dashboardIdFromUrl]);
+
+  return {
+    ...state,
+  };
+};
diff --git a/src/plugins/dashboard/public/application/utils/utils.ts b/src/plugins/dashboard/public/application/utils/utils.ts
new file mode 100644
index 000000000000..9a337585dec0
--- /dev/null
+++ b/src/plugins/dashboard/public/application/utils/utils.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Filter } from 'src/plugins/data/public';
+import { DashboardServices } from '../../types';
+
+export const getDefaultQuery = ({ data }: DashboardServices) => {
+  return data.query.queryString.getDefaultQuery();
+};
+
+export const dashboardStateToEditorState = (
+  dashboardInstance: any,
+  services: DashboardServices
+) => {
+  const savedDashboardState = {
+    id: dashboardInstance.id,
+    title: dashboardInstance.title,
+    description: dashboardInstance.description,
+    searchSource: dashboardInstance.searchSource,
+    savedSearchId: dashboardInstance.savedSearchId,
+  };
+  return {
+    query: dashboardInstance.searchSource?.getOwnField('query') || getDefaultQuery(services),
+    filters: (dashboardInstance.searchSource?.getOwnField('filter') as Filter[]) || [],
+    savedDashboardState,
+  };
+};
diff --git a/src/plugins/dashboard/public/types.ts b/src/plugins/dashboard/public/types.ts
index 6ba53fe9e050..8a94a424b940 100644
--- a/src/plugins/dashboard/public/types.ts
+++ b/src/plugins/dashboard/public/types.ts
@@ -40,7 +40,11 @@ import {
   ScopedHistory,
   AppMountParameters,
 } from 'src/core/public';
-import { IOsdUrlStateStorage, Storage } from 'src/plugins/opensearch_dashboards_utils/public';
+import {
+  IOsdUrlStateStorage,
+  ReduxLikeStateContainer,
+  Storage,
+} from 'src/plugins/opensearch_dashboards_utils/public';
 import { SavedObjectLoader, SavedObjectsStart } from 'src/plugins/saved_objects/public';
 import { OpenSearchDashboardsLegacyStart } from 'src/plugins/opensearch_dashboards_legacy/public';
 import { SharePluginStart } from 'src/plugins/share/public';
@@ -54,6 +58,8 @@ import { SavedDashboardPanel730ToLatest } from '../common';
 export interface DashboardCapabilities {
   showWriteControls: boolean;
   createNew: boolean;
+  showSavedQuery: boolean;
+  saveQuery: boolean;
 }
 
 // TODO: Replace Saved object interfaces by the ones Core will provide when it is ready.
@@ -149,6 +155,11 @@ export interface DashboardAppStateTransitions {
   ) => DashboardAppState;
 }
 
+export type DashboardAppStateContainer = ReduxLikeStateContainer<
+  DashboardAppState,
+  DashboardAppStateTransitions
+>;
+
 export interface SavedDashboardPanelMap {
   [key: string]: SavedDashboardPanel;
 }