diff --git a/frontend/src/metabase-types/store/embed.ts b/frontend/src/metabase-types/store/embed.ts
index 1e5bb3414f82a05fc61dcd8903d054bb7fd0324f..989a67e68e3a8af5a844963b746474aebf3a38da 100644
--- a/frontend/src/metabase-types/store/embed.ts
+++ b/frontend/src/metabase-types/store/embed.ts
@@ -3,6 +3,9 @@ export interface EmbedOptions {
   search?: boolean;
   new_button?: boolean;
   side_nav?: boolean | "default";
+  header?: boolean;
+  additional_info?: boolean;
+  action_buttons?: boolean;
 }
 
 export interface EmbedState {
diff --git a/frontend/src/metabase/App.styled.tsx b/frontend/src/metabase/App.styled.tsx
index b85d2e19b2b1b6c295fcee312961feaf6cf0cadb..df8fb28b6f620e51f1ef2c4def705b00183882ba 100644
--- a/frontend/src/metabase/App.styled.tsx
+++ b/frontend/src/metabase/App.styled.tsx
@@ -5,14 +5,14 @@ import { APP_BAR_HEIGHT } from "metabase/nav/constants";
 
 export const AppContentContainer = styled.div<{
   isAdminApp: boolean;
-  hasAppBar: boolean;
+  isAppBarVisible: boolean;
 }>`
   display: flex;
   flex-direction: ${props => (props.isAdminApp ? "column" : "row")};
   position: relative;
   overflow: hidden;
   height: ${props =>
-    props.hasAppBar ? `calc(100vh - ${APP_BAR_HEIGHT})` : "100vh"};
+    props.isAppBarVisible ? `calc(100vh - ${APP_BAR_HEIGHT})` : "100vh"};
   background-color: ${props =>
     color(props.isAdminApp ? "bg-white" : "content")};
 `;
diff --git a/frontend/src/metabase/App.tsx b/frontend/src/metabase/App.tsx
index 98e2c390440c1fb40a6126531861421d14ee278c..99284b8467c2328ed88d8d832174dc94b185077a 100644
--- a/frontend/src/metabase/App.tsx
+++ b/frontend/src/metabase/App.tsx
@@ -1,4 +1,4 @@
-import React, { ErrorInfo, ReactNode, useMemo, useState } from "react";
+import React, { ErrorInfo, ReactNode, useState } from "react";
 import { connect } from "react-redux";
 import { Location } from "history";
 
@@ -7,27 +7,28 @@ import AppErrorCard from "metabase/components/AppErrorCard/AppErrorCard";
 import ScrollToTop from "metabase/hoc/ScrollToTop";
 import {
   Archived,
-  NotFound,
   GenericError,
+  NotFound,
   Unauthorized,
 } from "metabase/containers/ErrorPages";
 import UndoListing from "metabase/containers/UndoListing";
 
-import { getErrorPage } from "metabase/selectors/app";
-import { getEmbedOptions } from "metabase/selectors/embed";
-import { getUser } from "metabase/selectors/user";
-import { getIsEditing as getIsEditingDashboard } from "metabase/dashboard/selectors";
+import {
+  getErrorPage,
+  getIsAdminApp,
+  getIsAppBarVisible,
+  getIsNavBarVisible,
+} from "metabase/selectors/app";
 import { useOnMount } from "metabase/hooks/use-on-mount";
-import { IFRAMED, initializeIframeResizer } from "metabase/lib/dom";
+import { initializeIframeResizer } from "metabase/lib/dom";
 
 import AppBar from "metabase/nav/containers/AppBar";
 import Navbar from "metabase/nav/containers/Navbar";
 import StatusListing from "metabase/status/containers/StatusListing";
 
-import { User } from "metabase-types/api";
-import { AppErrorDescriptor, EmbedOptions, State } from "metabase-types/store";
+import { AppErrorDescriptor, State } from "metabase-types/store";
 
-import { AppContentContainer, AppContent } from "./App.styled";
+import { AppContent, AppContentContainer } from "./App.styled";
 
 const getErrorComponent = ({ status, data, context }: AppErrorDescriptor) => {
   if (status === 403 || data?.error_code === "unauthorized") {
@@ -45,21 +46,11 @@ const getErrorComponent = ({ status, data, context }: AppErrorDescriptor) => {
   return <GenericError details={data?.message} />;
 };
 
-const PATHS_WITHOUT_NAVBAR = [/\/model\/.*\/query/, /\/model\/.*\/metadata/];
-
-const HOMEPAGE_PATTERN = /^\/$/;
-const EMBEDDED_ROUTES_WITH_NAVBAR = [
-  HOMEPAGE_PATTERN,
-  /^\/collection\/.*/,
-  /^\/archive/,
-];
-
 interface AppStateProps {
-  currentUser?: User;
   errorPage: AppErrorDescriptor | null;
-  isEditingDashboard: boolean;
-  isEmbedded: boolean;
-  embedOptions: EmbedOptions;
+  isAdminApp: boolean;
+  isAppBarVisible: boolean;
+  isNavBarVisible: boolean;
 }
 
 interface AppRouterOwnProps {
@@ -69,15 +60,15 @@ interface AppRouterOwnProps {
 
 type AppProps = AppStateProps & AppRouterOwnProps;
 
-function mapStateToProps(state: State): AppStateProps {
-  return {
-    currentUser: getUser(state),
-    errorPage: getErrorPage(state),
-    isEditingDashboard: getIsEditingDashboard(state),
-    isEmbedded: IFRAMED,
-    embedOptions: getEmbedOptions(state),
-  };
-}
+const mapStateToProps = (
+  state: State,
+  props: AppRouterOwnProps,
+): AppStateProps => ({
+  errorPage: getErrorPage(state),
+  isAdminApp: getIsAdminApp(state, props),
+  isAppBarVisible: getIsAppBarVisible(state, props),
+  isNavBarVisible: getIsNavBarVisible(state, props),
+});
 
 class ErrorBoundary extends React.Component<{
   onError: (errorInfo: ErrorInfo) => void;
@@ -92,12 +83,10 @@ class ErrorBoundary extends React.Component<{
 }
 
 function App({
-  currentUser,
   errorPage,
-  location: { pathname, hash },
-  isEditingDashboard,
-  isEmbedded,
-  embedOptions,
+  isAdminApp,
+  isAppBarVisible,
+  isNavBarVisible,
   children,
 }: AppProps) {
   const [errorInfo, setErrorInfo] = useState<ErrorInfo | null>(null);
@@ -106,52 +95,16 @@ function App({
     initializeIframeResizer();
   });
 
-  const isAdminApp = useMemo(() => pathname.startsWith("/admin/"), [pathname]);
-
-  const hasNavbar = useMemo(() => {
-    if (!currentUser || isEditingDashboard) {
-      return false;
-    }
-    if (isEmbedded && !embedOptions.side_nav) {
-      return false;
-    }
-    if (isEmbedded && embedOptions.side_nav === "default") {
-      return EMBEDDED_ROUTES_WITH_NAVBAR.some(pattern =>
-        pattern.test(pathname),
-      );
-    }
-    return !PATHS_WITHOUT_NAVBAR.some(pattern => pattern.test(pathname));
-  }, [currentUser, pathname, isEditingDashboard, isEmbedded, embedOptions]);
-
-  const hasAppBar = useMemo(() => {
-    const isFullscreen = hash.includes("fullscreen");
-    if (
-      !currentUser ||
-      (isEmbedded && !embedOptions.top_nav) ||
-      isAdminApp ||
-      isEditingDashboard ||
-      isFullscreen
-    ) {
-      return false;
-    }
-    return !PATHS_WITHOUT_NAVBAR.some(pattern => pattern.test(pathname));
-  }, [
-    currentUser,
-    pathname,
-    isEditingDashboard,
-    isEmbedded,
-    embedOptions,
-    isAdminApp,
-    hash,
-  ]);
-
   return (
     <ErrorBoundary onError={setErrorInfo}>
       <ScrollToTop>
         <div className="spread">
-          {hasAppBar && <AppBar />}
-          <AppContentContainer hasAppBar={hasAppBar} isAdminApp={isAdminApp}>
-            {hasNavbar && <Navbar />}
+          {isAppBarVisible && <AppBar />}
+          <AppContentContainer
+            isAdminApp={isAdminApp}
+            isAppBarVisible={isAppBarVisible}
+          >
+            {isNavBarVisible && <Navbar />}
             <AppContent>
               {errorPage ? getErrorComponent(errorPage) : children}
             </AppContent>
diff --git a/frontend/src/metabase/components/Header.jsx b/frontend/src/metabase/components/Header.jsx
index ab8d9ff76ab38f2515cda8ad539176e11ba240da..577c2df18bc8f18bcac68940bbeb9875df17b70d 100644
--- a/frontend/src/metabase/components/Header.jsx
+++ b/frontend/src/metabase/components/Header.jsx
@@ -18,6 +18,7 @@ import {
   HeaderButtonsContainer,
   HeaderButtonSection,
   StyledLastEditInfoLabel,
+  HeaderCaption,
 } from "./Header.styled";
 
 const propTypes = {
@@ -33,7 +34,8 @@ const propTypes = {
   isEditingInfo: PropTypes.bool,
   item: PropTypes.object.isRequired,
   objectType: PropTypes.string.isRequired,
-  hasBadge: PropTypes.bool,
+  isBadgeVisible: PropTypes.bool,
+  isLastEditInfoVisible: PropTypes.bool,
   children: PropTypes.node,
   setItemAttributeFn: PropTypes.func,
   onHeaderModalDone: PropTypes.func,
@@ -117,8 +119,12 @@ class Header extends Component {
   }
 
   render() {
-    const { item, hasBadge, onLastEditInfoClick } = this.props;
-    const hasLastEditInfo = !!item["last-edit-info"];
+    const {
+      item,
+      isBadgeVisible,
+      isLastEditInfoVisible,
+      onLastEditInfoClick,
+    } = this.props;
 
     let titleAndDescription;
     if (this.props.item && this.props.item.id != null) {
@@ -172,10 +178,10 @@ class Header extends Component {
           ref={this.header}
         >
           <HeaderContent>
-            <span className="inline-block mb1">{titleAndDescription}</span>
+            <HeaderCaption>{titleAndDescription}</HeaderCaption>
             {attribution}
             <HeaderBadges>
-              {hasBadge && (
+              {isBadgeVisible && (
                 <>
                   <CollectionBadge
                     collectionId={item.collection_id}
@@ -183,10 +189,10 @@ class Header extends Component {
                   />
                 </>
               )}
-              {hasBadge && hasLastEditInfo && (
+              {isBadgeVisible && isLastEditInfoVisible && (
                 <HeaderBadgesDivider>•</HeaderBadgesDivider>
               )}
-              {hasLastEditInfo && (
+              {isLastEditInfoVisible && (
                 <StyledLastEditInfoLabel
                   item={item}
                   onClick={onLastEditInfoClick}
diff --git a/frontend/src/metabase/components/Header.styled.tsx b/frontend/src/metabase/components/Header.styled.tsx
index 7599ced7e66dc30f4d65bee4bb74cfe696a7da07..ef21de88cf3284425482296d06a8324883dcdd3a 100644
--- a/frontend/src/metabase/components/Header.styled.tsx
+++ b/frontend/src/metabase/components/Header.styled.tsx
@@ -22,6 +22,14 @@ export const HeaderContent = styled.div`
   padding: 1rem 0;
 `;
 
+export const HeaderCaption = styled.span`
+  display: inline-block;
+
+  &:not(:last-child) {
+    margin-bottom: 0.5rem;
+  }
+`;
+
 export const HeaderBadges = styled.div`
   display: flex;
   align-items: center;
diff --git a/frontend/src/metabase/dashboard/components/Dashboard/Dashboard.jsx b/frontend/src/metabase/dashboard/components/Dashboard/Dashboard.jsx
index 5740bcfeed4c1ed175157a1d8385d203bc3fc718..9fbeff62a6117e13cbc050a774a1ebced8920748 100644
--- a/frontend/src/metabase/dashboard/components/Dashboard/Dashboard.jsx
+++ b/frontend/src/metabase/dashboard/components/Dashboard/Dashboard.jsx
@@ -46,6 +46,8 @@ class Dashboard extends Component {
       .isRequired,
     isEditingParameter: PropTypes.bool.isRequired,
     isNavbarOpen: PropTypes.bool.isRequired,
+    isHeaderVisible: PropTypes.bool,
+    isAdditionalInfoVisible: PropTypes.bool,
 
     dashboard: PropTypes.object,
     dashboardId: PropTypes.number,
@@ -220,6 +222,7 @@ class Dashboard extends Component {
       setParameterValue,
       setParameterIndex,
       setEditingParameter,
+      isHeaderVisible,
     } = this.props;
 
     const { error, isParametersWidgetSticky } = this.state;
@@ -261,30 +264,32 @@ class Dashboard extends Component {
       >
         {() => (
           <DashboardStyled>
-            <HeaderContainer
-              isFullscreen={isFullscreen}
-              isNightMode={shouldRenderAsNightMode}
-            >
-              <DashboardHeader
-                {...this.props}
-                onEditingChange={this.setEditing}
-                setDashboardAttribute={this.setDashboardAttribute}
-                addParameter={addParameter}
-                parametersWidget={parametersWidget}
-                onSharingClick={this.onSharingClick}
-                onToggleAddQuestionSidebar={this.onToggleAddQuestionSidebar}
-                showAddQuestionSidebar={showAddQuestionSidebar}
-              />
-
-              {shouldRenderParametersWidgetInEditMode && (
-                <ParametersWidgetContainer
-                  data-testid="edit-dashboard-parameters-widget-container"
-                  isEditing={isEditing}
-                >
-                  {parametersWidget}
-                </ParametersWidgetContainer>
-              )}
-            </HeaderContainer>
+            {isHeaderVisible && (
+              <HeaderContainer
+                isFullscreen={isFullscreen}
+                isNightMode={shouldRenderAsNightMode}
+              >
+                <DashboardHeader
+                  {...this.props}
+                  onEditingChange={this.setEditing}
+                  setDashboardAttribute={this.setDashboardAttribute}
+                  addParameter={addParameter}
+                  parametersWidget={parametersWidget}
+                  onSharingClick={this.onSharingClick}
+                  onToggleAddQuestionSidebar={this.onToggleAddQuestionSidebar}
+                  showAddQuestionSidebar={showAddQuestionSidebar}
+                />
+
+                {shouldRenderParametersWidgetInEditMode && (
+                  <ParametersWidgetContainer
+                    data-testid="edit-dashboard-parameters-widget-container"
+                    isEditing={isEditing}
+                  >
+                    {parametersWidget}
+                  </ParametersWidgetContainer>
+                )}
+              </HeaderContainer>
+            )}
 
             <DashboardBody isEditingOrSharing={isEditing || isSharing}>
               <ParametersAndCardsContainer
diff --git a/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx b/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx
index 70bb063098a90545da23be3a993163e206687375..4fb61eea8d10b6b6963f67ec6642d69a2bb04f13 100644
--- a/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx
+++ b/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx
@@ -22,6 +22,7 @@ import { Dashboard } from "metabase/dashboard/containers/Dashboard";
 import SyncedParametersList from "metabase/parameters/components/SyncedParametersList/SyncedParametersList";
 
 import { getMetadata } from "metabase/selectors/metadata";
+import { getIsHeaderVisible } from "metabase/dashboard/selectors";
 
 import Collections from "metabase/entities/collections";
 import Dashboards from "metabase/entities/dashboards";
@@ -46,6 +47,7 @@ const getDashboardId = (state, { params: { splat }, location: { hash } }) =>
 const mapStateToProps = (state, props) => ({
   metadata: getMetadata(state),
   dashboardId: getDashboardId(state, props),
+  isHeaderVisible: getIsHeaderVisible(state),
 });
 
 const mapDispatchToProps = {
@@ -104,6 +106,7 @@ class AutomaticDashboardAppInner extends React.Component {
       parameters,
       parameterValues,
       setParameterValue,
+      isHeaderVisible,
     } = this.props;
     const { savedDashboardId } = this.state;
     // pull out "more" related items for displaying as a button at the bottom of the dashboard
@@ -119,34 +122,36 @@ class AutomaticDashboardAppInner extends React.Component {
         })}
       >
         <div className="" style={{ marginRight: hasSidebar ? 346 : undefined }}>
-          <div className="bg-white border-bottom py2">
-            <div className="wrapper flex align-center">
-              <Icon name="bolt" className="text-gold mr2" size={24} />
-              <div>
-                <h2 className="text-wrap mr2">
-                  {dashboard && <TransientTitle dashboard={dashboard} />}
-                </h2>
-                {dashboard && dashboard.transient_filters && (
-                  <TransientFilters
-                    filter={dashboard.transient_filters}
-                    metadata={this.props.metadata}
-                  />
+          {isHeaderVisible && (
+            <div className="bg-white border-bottom py2">
+              <div className="wrapper flex align-center">
+                <Icon name="bolt" className="text-gold mr2" size={24} />
+                <div>
+                  <h2 className="text-wrap mr2">
+                    {dashboard && <TransientTitle dashboard={dashboard} />}
+                  </h2>
+                  {dashboard && dashboard.transient_filters && (
+                    <TransientFilters
+                      filter={dashboard.transient_filters}
+                      metadata={this.props.metadata}
+                    />
+                  )}
+                </div>
+                {savedDashboardId != null ? (
+                  <Button className="ml-auto" disabled>{t`Saved`}</Button>
+                ) : (
+                  <ActionButton
+                    className="ml-auto text-nowrap"
+                    success
+                    borderless
+                    actionFn={this.save}
+                  >
+                    {t`Save this`}
+                  </ActionButton>
                 )}
               </div>
-              {savedDashboardId != null ? (
-                <Button className="ml-auto" disabled>{t`Saved`}</Button>
-              ) : (
-                <ActionButton
-                  className="ml-auto text-nowrap"
-                  success
-                  borderless
-                  actionFn={this.save}
-                >
-                  {t`Save this`}
-                </ActionButton>
-              )}
             </div>
-          </div>
+          )}
 
           <div className="wrapper pb4">
             {parameters && parameters.length > 0 && (
diff --git a/frontend/src/metabase/dashboard/containers/DashboardApp.jsx b/frontend/src/metabase/dashboard/containers/DashboardApp.jsx
index 6595edce5b37bf2531254a73069bf531e2cf34df..50669405b24f5ac7e283436b36d71f7237fbd625 100644
--- a/frontend/src/metabase/dashboard/containers/DashboardApp.jsx
+++ b/frontend/src/metabase/dashboard/containers/DashboardApp.jsx
@@ -41,6 +41,8 @@ import {
   getDocumentTitle,
   getIsRunning,
   getIsLoadingComplete,
+  getIsHeaderVisible,
+  getIsAdditionalInfoVisible,
 } from "../selectors";
 import { getDatabases, getMetadata } from "metabase/selectors/metadata";
 import {
@@ -83,6 +85,8 @@ const mapStateToProps = (state, props) => {
     documentTitle: getDocumentTitle(state),
     isRunning: getIsRunning(state),
     isLoadingComplete: getIsLoadingComplete(state),
+    isHeaderVisible: getIsHeaderVisible(state),
+    isAdditionalInfoVisible: getIsAdditionalInfoVisible(state),
   };
 };
 
diff --git a/frontend/src/metabase/dashboard/containers/DashboardHeader.jsx b/frontend/src/metabase/dashboard/containers/DashboardHeader.jsx
index 75099018b61b302c072ff740058d6df1762a7ddd..d4ea0805ddf9bf8cc59ab3550f190ad4815cf17a 100644
--- a/frontend/src/metabase/dashboard/containers/DashboardHeader.jsx
+++ b/frontend/src/metabase/dashboard/containers/DashboardHeader.jsx
@@ -56,6 +56,7 @@ class DashboardHeader extends Component {
       .isRequired,
     isFullscreen: PropTypes.bool.isRequired,
     isNightMode: PropTypes.bool.isRequired,
+    isAdditionalInfoVisible: PropTypes.bool,
 
     refreshPeriod: PropTypes.number,
     setRefreshElapsedHook: PropTypes.func.isRequired,
@@ -373,7 +374,16 @@ class DashboardHeader extends Component {
   }
 
   render() {
-    const { dashboard, location, onChangeLocation } = this.props;
+    const {
+      dashboard,
+      location,
+      isEditing,
+      isFullscreen,
+      isAdditionalInfoVisible,
+      onChangeLocation,
+    } = this.props;
+
+    const hasLastEditInfo = dashboard["last-edit-info"] != null;
 
     return (
       <Header
@@ -381,9 +391,10 @@ class DashboardHeader extends Component {
         objectType="dashboard"
         analyticsContext="Dashboard"
         item={dashboard}
-        isEditing={this.props.isEditing}
-        hasBadge={!this.props.isEditing && !this.props.isFullscreen}
-        isEditingInfo={this.props.isEditing}
+        isEditing={isEditing}
+        isBadgeVisible={!isEditing && !isFullscreen && isAdditionalInfoVisible}
+        isLastEditInfoVisible={hasLastEditInfo && isAdditionalInfoVisible}
+        isEditingInfo={isEditing}
         headerButtons={this.getHeaderButtons()}
         editWarning={this.getEditWarning(dashboard)}
         editingTitle={t`You're editing this dashboard.`}
diff --git a/frontend/src/metabase/dashboard/selectors.js b/frontend/src/metabase/dashboard/selectors.js
index dd9c3cbe4ed6880a5edf4ca3e3c28415ce2c32ad..2b16b060ae9a61e01d782132f874c7352eb0d1c9 100644
--- a/frontend/src/metabase/dashboard/selectors.js
+++ b/frontend/src/metabase/dashboard/selectors.js
@@ -13,6 +13,7 @@ import {
 import { getParameterMappingOptions as _getParameterMappingOptions } from "metabase/parameters/utils/mapping-options";
 
 import { SIDEBAR_NAME } from "metabase/dashboard/constants";
+import { getEmbedOptions, getIsEmbedded } from "metabase/selectors/embed";
 
 export const getDashboardId = state => state.dashboard.dashboardId;
 export const getIsEditing = state => !!state.dashboard.isEditing;
@@ -194,3 +195,13 @@ export const getDashboardParameterValuesCache = state => {
     },
   };
 };
+
+export const getIsHeaderVisible = createSelector(
+  [getIsEmbedded, getEmbedOptions],
+  (isEmbedded, embedOptions) => !isEmbedded || embedOptions.header,
+);
+
+export const getIsAdditionalInfoVisible = createSelector(
+  [getIsEmbedded, getEmbedOptions],
+  (isEmbedded, embedOptions) => !isEmbedded || embedOptions.additional_info,
+);
diff --git a/frontend/src/metabase/query_builder/components/view/QuestionFilters.jsx b/frontend/src/metabase/query_builder/components/view/QuestionFilters.jsx
index 1c5b3bfeb6ed0cce3b2c7e39c140f6ca9a9d4b7d..2fd2d67dfc3ba319b3ddd96c62115bfd8770dc06 100644
--- a/frontend/src/metabase/query_builder/components/view/QuestionFilters.jsx
+++ b/frontend/src/metabase/query_builder/components/view/QuestionFilters.jsx
@@ -205,8 +205,10 @@ QuestionFilterWidget.shouldRender = ({
   question,
   queryBuilderMode,
   isObjectDetail,
+  isActionListVisible,
 }) =>
   queryBuilderMode === "view" &&
   question.isStructured() &&
   question.query().isEditable() &&
-  !isObjectDetail;
+  !isObjectDetail &&
+  isActionListVisible;
diff --git a/frontend/src/metabase/query_builder/components/view/QuestionNotebookButton.jsx b/frontend/src/metabase/query_builder/components/view/QuestionNotebookButton.jsx
index 7597670445af2b8b89aff27ada4dc3c9ddbf2253..729b3ba601cf09822ac9db20750e7fbf3a0ca44e 100644
--- a/frontend/src/metabase/query_builder/components/view/QuestionNotebookButton.jsx
+++ b/frontend/src/metabase/query_builder/components/view/QuestionNotebookButton.jsx
@@ -14,7 +14,7 @@ export default function QuestionNotebookButton({
   setQueryBuilderMode,
   ...props
 }) {
-  return QuestionNotebookButton.shouldRender({ question }) ? (
+  return (
     <Tooltip
       tooltip={isShowingNotebook ? t`Hide editor` : t`Show editor`}
       placement="bottom"
@@ -33,8 +33,10 @@ export default function QuestionNotebookButton({
         {...props}
       />
     </Tooltip>
-  ) : null;
+  );
 }
 
-QuestionNotebookButton.shouldRender = ({ question }) =>
-  question.isStructured() && question.query().isEditable();
+QuestionNotebookButton.shouldRender = ({ question, isActionListVisible }) =>
+  question.isStructured() &&
+  question.query().isEditable() &&
+  isActionListVisible;
diff --git a/frontend/src/metabase/query_builder/components/view/QuestionSummaries.jsx b/frontend/src/metabase/query_builder/components/view/QuestionSummaries.jsx
index 44149f89158c772bb5ff03961437bb5bc0318508..464a7551f021c3d75e29e6d217d3121aec74980e 100644
--- a/frontend/src/metabase/query_builder/components/view/QuestionSummaries.jsx
+++ b/frontend/src/metabase/query_builder/components/view/QuestionSummaries.jsx
@@ -104,10 +104,12 @@ QuestionSummarizeWidget.shouldRender = ({
   question,
   queryBuilderMode,
   isObjectDetail,
+  isActionListVisible,
 }) =>
   queryBuilderMode === "view" &&
   question &&
   question.isStructured() &&
   question.query().isEditable() &&
   question.query().table() &&
-  !isObjectDetail;
+  !isObjectDetail &&
+  isActionListVisible;
diff --git a/frontend/src/metabase/query_builder/components/view/View.jsx b/frontend/src/metabase/query_builder/components/view/View.jsx
index 4fe23e07e9f1be2b62e882d526a7a38f3cb8a84b..7e222bf644272fc8fa91db8f45c180fba243f342 100644
--- a/frontend/src/metabase/query_builder/components/view/View.jsx
+++ b/frontend/src/metabase/query_builder/components/view/View.jsx
@@ -40,12 +40,12 @@ import NewQuestionView from "./View/NewQuestionView";
 import QueryViewNotebook from "./View/QueryViewNotebook";
 
 import {
-  QueryBuilderViewRoot,
+  BorderedViewTitleHeader,
+  NativeQueryEditorContainer,
   QueryBuilderContentContainer,
   QueryBuilderMain,
   QueryBuilderViewHeaderContainer,
-  BorderedViewTitleHeader,
-  NativeQueryEditorContainer,
+  QueryBuilderViewRoot,
   StyledDebouncedFrame,
   StyledSyncedParametersList,
 } from "./View.styled";
@@ -454,6 +454,7 @@ class View extends React.Component {
       onDismissToast,
       onConfirmToast,
       isShowingToaster,
+      isHeaderVisible,
     } = this.props;
 
     // if we don't have a card at all or no databases then we are initializing, so keep it simple
@@ -486,7 +487,7 @@ class View extends React.Component {
     return (
       <div className="full-height">
         <QueryBuilderViewRoot className="QueryBuilder">
-          {this.renderHeader()}
+          {isHeaderVisible && this.renderHeader()}
           <QueryBuilderContentContainer>
             {isStructured && (
               <QueryViewNotebook
diff --git a/frontend/src/metabase/query_builder/components/view/ViewHeader.jsx b/frontend/src/metabase/query_builder/components/view/ViewHeader.jsx
index 1e761eaaafc74f2a955b0e89ecdc0773329a893e..c1b04127f2972fd9351be4fd88ca0c3670dc87aa 100644
--- a/frontend/src/metabase/query_builder/components/view/ViewHeader.jsx
+++ b/frontend/src/metabase/query_builder/components/view/ViewHeader.jsx
@@ -63,6 +63,7 @@ const viewTitleHeaderPropTypes = {
   isShowingSummarySidebar: PropTypes.bool,
   isShowingQuestionDetailsSidebar: PropTypes.bool,
   isObjectDetail: PropTypes.bool,
+  isAdditionalInfoVisible: PropTypes.bool,
 
   runQuestionQuery: PropTypes.func,
   cancelQuery: PropTypes.func,
@@ -107,8 +108,6 @@ export function ViewTitleHeader(props) {
     }
   }, [previousQuestion, question, expandFilters]);
 
-  const lastEditInfo = question.lastEditInfo();
-
   const isStructured = question.isStructured();
   const isNative = question.isNative();
   const isSaved = question.isSaved();
@@ -131,7 +130,7 @@ export function ViewTitleHeader(props) {
         {isDataset ? (
           <DatasetLeftSide {...props} />
         ) : isSaved ? (
-          <SavedQuestionLeftSide {...props} lastEditInfo={lastEditInfo} />
+          <SavedQuestionLeftSide {...props} />
         ) : (
           <AhHocQuestionLeftSide
             {...props}
@@ -163,9 +162,9 @@ export function ViewTitleHeader(props) {
 
 SavedQuestionLeftSide.propTypes = {
   question: PropTypes.object.isRequired,
-  lastEditInfo: PropTypes.object,
-  isShowingQuestionDetailsSidebar: PropTypes.bool,
   isObjectDetail: PropTypes.bool,
+  isAdditionalInfoVisible: PropTypes.bool,
+  isShowingQuestionDetailsSidebar: PropTypes.bool,
   onOpenQuestionDetails: PropTypes.func.isRequired,
   onCloseQuestionDetails: PropTypes.func.isRequired,
   onOpenQuestionHistory: PropTypes.func.isRequired,
@@ -175,13 +174,15 @@ function SavedQuestionLeftSide(props) {
   const {
     question,
     isObjectDetail,
+    isAdditionalInfoVisible,
     isShowingQuestionDetailsSidebar,
     onOpenQuestionDetails,
     onCloseQuestionDetails,
-    lastEditInfo,
     onOpenQuestionHistory,
   } = props;
 
+  const hasLastEditInfo = question.lastEditInfo() != null;
+
   const onHeaderClick = useCallback(() => {
     if (isShowingQuestionDetailsSidebar) {
       onCloseQuestionDetails();
@@ -204,23 +205,25 @@ function SavedQuestionLeftSide(props) {
             onClick={onHeaderClick}
           />
         </SavedQuestionHeaderButtonContainer>
-        {lastEditInfo && (
+        {hasLastEditInfo && isAdditionalInfoVisible && (
           <StyledLastEditInfoLabel
             item={question.card()}
             onClick={onOpenQuestionHistory}
           />
         )}
       </ViewHeaderMainLeftContentContainer>
-      <ViewHeaderLeftSubHeading>
-        <StyledCollectionBadge collectionId={question.collectionId()} />
-        {QuestionDataSource.shouldRender(props) && (
-          <StyledQuestionDataSource
-            question={question}
-            isObjectDetail={isObjectDetail}
-            subHead
-          />
-        )}
-      </ViewHeaderLeftSubHeading>
+      {isAdditionalInfoVisible && (
+        <ViewHeaderLeftSubHeading>
+          <StyledCollectionBadge collectionId={question.collectionId()} />
+          {QuestionDataSource.shouldRender(props) && (
+            <StyledQuestionDataSource
+              question={question}
+              isObjectDetail={isObjectDetail}
+              subHead
+            />
+          )}
+        </ViewHeaderLeftSubHeading>
+      )}
     </div>
   );
 }
@@ -279,6 +282,7 @@ function AhHocQuestionLeftSide(props) {
 
 DatasetLeftSide.propTypes = {
   question: PropTypes.object.isRequired,
+  isAdditionalInfoVisible: PropTypes.bool,
   isShowingQuestionDetailsSidebar: PropTypes.bool,
   onOpenQuestionDetails: PropTypes.func.isRequired,
   onCloseQuestionDetails: PropTypes.func.isRequired,
@@ -287,6 +291,7 @@ DatasetLeftSide.propTypes = {
 function DatasetLeftSide(props) {
   const {
     question,
+    isAdditionalInfoVisible,
     isShowingQuestionDetailsSidebar,
     onOpenQuestionDetails,
     onCloseQuestionDetails,
@@ -311,7 +316,14 @@ function DatasetLeftSide(props) {
           <HeadBreadcrumbs
             divider="/"
             parts={[
-              <DatasetCollectionBadge key="collection" dataset={question} />,
+              ...(isAdditionalInfoVisible
+                ? [
+                    <DatasetCollectionBadge
+                      key="collection"
+                      dataset={question}
+                    />,
+                  ]
+                : []),
               <DatasetHeaderButtonContainer key="dataset-header-button">
                 <SavedQuestionHeaderButton
                   question={question}
@@ -355,6 +367,7 @@ ViewTitleHeaderRightSide.propTypes = {
   isShowingSummarySidebar: PropTypes.bool,
   isDirty: PropTypes.bool,
   isResultDirty: PropTypes.bool,
+  isActionListVisible: PropTypes.bool,
   runQuestionQuery: PropTypes.func,
   cancelQuery: PropTypes.func,
   onOpenModal: PropTypes.func,
@@ -384,6 +397,7 @@ function ViewTitleHeaderRightSide(props) {
     isShowingSummarySidebar,
     isDirty,
     isResultDirty,
+    isActionListVisible,
     runQuestionQuery,
     cancelQuery,
     onOpenModal,
@@ -408,7 +422,11 @@ function ViewTitleHeaderRightSide(props) {
     MetabaseSettings.get("enable-nested-queries");
 
   const isNewQuery = !query.hasData();
-  const hasSaveButton = !isDataset && !!isDirty && (isNewQuery || canEditQuery);
+  const hasSaveButton =
+    !isDataset &&
+    !!isDirty &&
+    (isNewQuery || canEditQuery) &&
+    isActionListVisible;
   const isMissingPermissions =
     result?.error_type === SERVER_ERROR_TYPES.missingPermissions;
   const hasRunButton =
@@ -466,7 +484,7 @@ function ViewTitleHeaderRightSide(props) {
           data-metabase-event={`View Mode; Open Summary Widget`}
         />
       )}
-      {QuestionNotebookButton.shouldRender({ question }) && (
+      {QuestionNotebookButton.shouldRender(props) && (
         <QuestionNotebookButton
           className="hide sm-show"
           ml={2}
diff --git a/frontend/src/metabase/query_builder/components/view/ViewHeader.unit.spec.js b/frontend/src/metabase/query_builder/components/view/ViewHeader.unit.spec.js
index acecc634dfb44337dee98da0d4bb5e0fc643e462..189709804c346a98ac50ddb281443c04489b46d7 100644
--- a/frontend/src/metabase/query_builder/components/view/ViewHeader.unit.spec.js
+++ b/frontend/src/metabase/query_builder/components/view/ViewHeader.unit.spec.js
@@ -94,7 +94,14 @@ function mockSettings({ enableNestedQueries = true } = {}) {
   });
 }
 
-function setup({ question, isRunnable = true, settings, ...props } = {}) {
+function setup({
+  question,
+  settings,
+  isRunnable = true,
+  isActionListVisible = true,
+  isAdditionalInfoVisible = true,
+  ...props
+} = {}) {
   mockSettings(settings);
 
   const callbacks = {
@@ -113,8 +120,10 @@ function setup({ question, isRunnable = true, settings, ...props } = {}) {
     <ViewTitleHeader
       {...callbacks}
       {...props}
-      isRunnable={isRunnable}
       question={question}
+      isRunnable={isRunnable}
+      isActionListVisible={isActionListVisible}
+      isAdditionalInfoVisible={isAdditionalInfoVisible}
     />,
     {
       withRouter: true,
diff --git a/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx b/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx
index 5d7b99d8bcdfbdd5210cd895e4162b1f7b0114dd..e1f610aba3ba4e73881e21500a03ae08bfbf6402 100644
--- a/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx
+++ b/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx
@@ -87,6 +87,9 @@ import {
   getPageFavicon,
   getIsTimeseries,
   getIsLoadingComplete,
+  getIsHeaderVisible,
+  getIsActionListVisible,
+  getIsAdditionalInfoVisible,
 } from "../selectors";
 import * as actions from "../actions";
 
@@ -162,6 +165,9 @@ const mapStateToProps = (state, props) => {
     isVisualized: getIsVisualized(state),
     isLiveResizable: getIsLiveResizable(state),
     isTimeseries: getIsTimeseries(state),
+    isHeaderVisible: getIsHeaderVisible(state),
+    isActionListVisible: getIsActionListVisible(state),
+    isAdditionalInfoVisible: getIsAdditionalInfoVisible(state),
 
     parameters: getParameters(state),
     databaseFields: getDatabaseFields(state),
diff --git a/frontend/src/metabase/query_builder/selectors.js b/frontend/src/metabase/query_builder/selectors.js
index 3bedb3439d01d9a0088e4f37f28e556ed81a62c2..f2610c131563aa1a8e86c23ab846f5f53c14e225 100644
--- a/frontend/src/metabase/query_builder/selectors.js
+++ b/frontend/src/metabase/query_builder/selectors.js
@@ -26,6 +26,7 @@ import Timelines from "metabase/entities/timelines";
 
 import { getMetadata } from "metabase/selectors/metadata";
 import { getAlerts } from "metabase/alert/selectors";
+import { getEmbedOptions, getIsEmbedded } from "metabase/selectors/embed";
 import { parseTimestamp } from "metabase/lib/time";
 import { getSortedTimelines } from "metabase/lib/timelines";
 import {
@@ -828,3 +829,18 @@ export const getTimeoutId = createSelector(
   [getLoadingControls],
   loadingControls => loadingControls.timeoutId,
 );
+
+export const getIsHeaderVisible = createSelector(
+  [getIsEmbedded, getEmbedOptions],
+  (isEmbedded, embedOptions) => !isEmbedded || embedOptions.header,
+);
+
+export const getIsActionListVisible = createSelector(
+  [getIsEmbedded, getEmbedOptions],
+  (isEmbedded, embedOptions) => !isEmbedded || embedOptions.action_buttons,
+);
+
+export const getIsAdditionalInfoVisible = createSelector(
+  [getIsEmbedded, getEmbedOptions],
+  (isEmbedded, embedOptions) => !isEmbedded || embedOptions.additional_info,
+);
diff --git a/frontend/src/metabase/redux/embed.js b/frontend/src/metabase/redux/embed.js
index 48cd5024d4b66f9cf7ea78016d4ac6e4c6a87be1..38f219f4008e0e027d47b59cf922237ccadeb398 100644
--- a/frontend/src/metabase/redux/embed.js
+++ b/frontend/src/metabase/redux/embed.js
@@ -9,6 +9,9 @@ const DEFAULT_OPTIONS = {
   side_nav: "default",
   search: false,
   new_button: false,
+  header: true,
+  additional_info: true,
+  action_buttons: true,
 };
 
 export const SET_OPTIONS = "metabase/embed/SET_OPTIONS";
diff --git a/frontend/src/metabase/selectors/app.ts b/frontend/src/metabase/selectors/app.ts
index 2ff6dee29fad54908faab9efbf188f38a596f469..5b74ba662d1d86b8648d18df7040ad2bc9e004bc 100644
--- a/frontend/src/metabase/selectors/app.ts
+++ b/frontend/src/metabase/selectors/app.ts
@@ -1,6 +1,92 @@
+import { Location } from "history";
+import { createSelector } from "reselect";
+import { getUser } from "metabase/selectors/user";
+import { getIsEditing as getIsEditingDashboard } from "metabase/dashboard/selectors";
+import { getEmbedOptions, getIsEmbedded } from "metabase/selectors/embed";
 import { State } from "metabase-types/store";
 
-export const getErrorPage = (state: State) => state.app.errorPage;
+interface RouterProps {
+  location: Location;
+}
+
+const HOMEPAGE_PATH = /^\/$/;
+const PATHS_WITHOUT_NAVBAR = [/\/model\/.*\/query/, /\/model\/.*\/metadata/];
+const EMBEDDED_PATHS_WITH_NAVBAR = [
+  HOMEPAGE_PATH,
+  /^\/collection\/.*/,
+  /^\/archive/,
+];
+
+export const getRouterPath = (state: State, props: RouterProps) => {
+  return props.location.pathname;
+};
+
+export const getRouterHash = (state: State, props: RouterProps) => {
+  return props.location.hash;
+};
+
+export const getIsAdminApp = createSelector([getRouterPath], path => {
+  return path.startsWith("/admin/");
+});
+
+export const getIsAppBarVisible = createSelector(
+  [
+    getUser,
+    getRouterPath,
+    getRouterHash,
+    getIsAdminApp,
+    getIsEditingDashboard,
+    getIsEmbedded,
+    getEmbedOptions,
+  ],
+  (
+    currentUser,
+    path,
+    hash,
+    isAdminApp,
+    isEditingDashboard,
+    isEmbedded,
+    embedOptions,
+  ) => {
+    const isFullscreen = hash.includes("fullscreen");
+    if (
+      !currentUser ||
+      (isEmbedded && !embedOptions.top_nav) ||
+      isAdminApp ||
+      isEditingDashboard ||
+      isFullscreen
+    ) {
+      return false;
+    }
+    return !PATHS_WITHOUT_NAVBAR.some(pattern => pattern.test(path));
+  },
+);
+
+export const getIsNavBarVisible = createSelector(
+  [
+    getUser,
+    getRouterPath,
+    getIsEditingDashboard,
+    getIsEmbedded,
+    getEmbedOptions,
+  ],
+  (currentUser, path, isEditingDashboard, isEmbedded, embedOptions) => {
+    if (!currentUser || isEditingDashboard) {
+      return false;
+    }
+    if (isEmbedded && !embedOptions.side_nav) {
+      return false;
+    }
+    if (isEmbedded && embedOptions.side_nav === "default") {
+      return EMBEDDED_PATHS_WITH_NAVBAR.some(pattern => pattern.test(path));
+    }
+    return !PATHS_WITHOUT_NAVBAR.some(pattern => pattern.test(path));
+  },
+);
+
+export const getErrorPage = (state: State) => {
+  return state.app.errorPage;
+};
 
 export const getErrorMessage = (state: State) => {
   const errorPage = getErrorPage(state);
diff --git a/frontend/src/metabase/selectors/embed.ts b/frontend/src/metabase/selectors/embed.ts
index a5e6ef8763eae5e95d6191a1965acc15dd0e0e48..0f5238bf6f4a049503774b71e621c1e98c803714 100644
--- a/frontend/src/metabase/selectors/embed.ts
+++ b/frontend/src/metabase/selectors/embed.ts
@@ -1,5 +1,10 @@
+import { IFRAMED } from "metabase/lib/dom";
 import { State } from "metabase-types/store";
 
+export const getIsEmbedded = () => {
+  return IFRAMED;
+};
+
 export const getEmbedOptions = (state: State) => {
   return state.embed.options;
 };
diff --git a/frontend/test/metabase/scenarios/embedding/embedding-full-app.cy.spec.js b/frontend/test/metabase/scenarios/embedding/embedding-full-app.cy.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..12fd757f865398df71ff37c88ce5426bc7dcbbe3
--- /dev/null
+++ b/frontend/test/metabase/scenarios/embedding/embedding-full-app.cy.spec.js
@@ -0,0 +1,162 @@
+import { restore } from "__support__/e2e/cypress";
+
+describe("scenarios > embedding > full app", () => {
+  beforeEach(() => {
+    restore();
+    cy.signInAsAdmin();
+    cy.intercept("POST", `/api/card/*/query`).as("getCardQuery");
+    cy.intercept("POST", "/api/dashboard/**/query").as("getDashCardQuery");
+    cy.intercept("GET", `/api/dashboard/*`).as("getDashboard");
+    cy.intercept("GET", "/api/automagic-dashboards/**").as("getXrayDashboard");
+  });
+
+  describe("navigation", () => {
+    it("should hide the top nav by default", () => {
+      visitUrl({ url: "/" });
+      cy.findByText("Our analytics").should("be.visible");
+      cy.findByTestId("main-logo").should("not.exist");
+    });
+
+    it("should show the top nav by a param", () => {
+      visitUrl({ url: "/", qs: { top_nav: true } });
+      cy.findAllByTestId("main-logo").should("be.visible");
+      cy.button(/New/).should("not.exist");
+      cy.findByPlaceholderText("Search").should("not.exist");
+    });
+
+    it("should hide the side nav by a param", () => {
+      visitUrl({ url: "/", qs: { top_nav: true, side_nav: false } });
+      cy.findAllByTestId("main-logo").should("be.visible");
+      cy.findByText("Our analytics").should("not.exist");
+    });
+
+    it("should show question creation controls by a param", () => {
+      visitUrl({ url: "/", qs: { top_nav: true, new_button: true } });
+      cy.button(/New/).should("be.visible");
+    });
+
+    it("should show search controls by a param", () => {
+      visitUrl({ url: "/", qs: { top_nav: true, search: true } });
+      cy.findByPlaceholderText("Search…").should("be.visible");
+    });
+
+    it("should preserve params when navigating", () => {
+      visitUrl({ url: "/", qs: { top_nav: true } });
+      cy.findAllByTestId("main-logo").should("be.visible");
+
+      cy.findByText("Our analytics").click();
+      cy.findByText("Orders in a dashboard").should("be.visible");
+      cy.findAllByTestId("main-logo").should("be.visible");
+    });
+  });
+
+  describe("questions", () => {
+    it("should show the question header by default", () => {
+      visitQuestionUrl({ url: "/question/1" });
+
+      cy.findByTestId("qb-header").should("be.visible");
+      cy.findByText(/Edited/).should("be.visible");
+      cy.findByText("Our analytics").should("be.visible");
+
+      cy.icon("refresh").should("be.visible");
+      cy.icon("notebook").should("be.visible");
+      cy.button("Summarize").should("be.visible");
+      cy.button("Filter").should("be.visible");
+    });
+
+    it("should hide the question header by a param", () => {
+      visitQuestionUrl({ url: "/question/1", qs: { header: false } });
+
+      cy.findByTestId("qb-header").should("not.exist");
+    });
+
+    it("should hide the question's additional info by a param", () => {
+      visitQuestionUrl({ url: "/question/1", qs: { additional_info: false } });
+
+      cy.findByText("Our analytics").should("not.exist");
+      cy.findByText(/Edited/).should("not.exist");
+    });
+
+    it("should hide the question's action buttons by a param", () => {
+      visitQuestionUrl({ url: "/question/1", qs: { action_buttons: false } });
+
+      cy.icon("refresh").should("be.visible");
+      cy.icon("notebook").should("not.exist");
+      cy.button("Summarize").should("not.exist");
+      cy.button("Filter").should("not.exist");
+    });
+  });
+
+  describe("dashboards", () => {
+    it("should show the dashboard header by default", () => {
+      visitDashboardUrl({ url: "/dashboard/1" });
+
+      cy.findByText("Orders in a dashboard").should("be.visible");
+      cy.findByText(/Edited/).should("be.visible");
+      cy.findByText("Our analytics").should("be.visible");
+    });
+
+    it("should hide the dashboard header by a param", () => {
+      visitDashboardUrl({ url: "/dashboard/1", qs: { header: false } });
+
+      cy.findByText("Orders in a dashboard").should("not.exist");
+    });
+
+    it("should hide the dashboard's additional info by a param", () => {
+      visitDashboardUrl({
+        url: "/dashboard/1",
+        qs: { additional_info: false },
+      });
+
+      cy.findByText("Orders in a dashboard").should("be.visible");
+      cy.findByText(/Edited/).should("not.exist");
+      cy.findByText("Our analytics").should("not.exist");
+    });
+  });
+
+  describe("x-ray dashboards", () => {
+    it("should show the dashboard header by default", () => {
+      visitXrayDashboardUrl({ url: "/auto/dashboard/table/1" });
+
+      cy.findByText("More X-rays").should("be.visible");
+      cy.button("Save this").should("be.visible");
+    });
+
+    it("should hide the dashboard header by a param", () => {
+      visitXrayDashboardUrl({
+        url: "/auto/dashboard/table/1",
+        qs: { header: false },
+      });
+
+      cy.findByText("More X-rays").should("be.visible");
+      cy.button("Save this").should("not.exist");
+    });
+  });
+});
+
+const visitUrl = url => {
+  cy.visit({
+    ...url,
+    onBeforeLoad(window) {
+      // cypress runs all tests in an iframe and the app uses this property to avoid embedding mode for all tests
+      // by removing the property the app would work in embedding mode
+      window.Cypress = undefined;
+    },
+  });
+};
+
+const visitQuestionUrl = url => {
+  visitUrl(url);
+  cy.wait("@getCardQuery");
+};
+
+const visitDashboardUrl = url => {
+  visitUrl(url);
+  cy.wait("@getDashboard");
+  cy.wait("@getDashCardQuery");
+};
+
+const visitXrayDashboardUrl = url => {
+  visitUrl(url);
+  cy.wait("@getXrayDashboard");
+};