diff --git a/frontend/src/metabase/lib/icon.ts b/frontend/src/metabase/lib/icon.ts
index 2c4919a9016583704b79868c78c078a7095bba99..7013d100547e9de903a7d29569cb9087b09b3060 100644
--- a/frontend/src/metabase/lib/icon.ts
+++ b/frontend/src/metabase/lib/icon.ts
@@ -9,7 +9,7 @@ import type {
   SearchModel,
 } from "metabase-types/api";
 
-type IconModel = SearchModel | CollectionItemModel | "schema";
+export type IconModel = SearchModel | CollectionItemModel | "schema";
 
 export type ObjectWithModel = {
   id?: unknown;
@@ -22,7 +22,7 @@ export type ObjectWithModel = {
   is_personal?: boolean;
 };
 
-const modelIconMap: Record<IconModel, IconName> = {
+export const modelIconMap: Record<IconModel, IconName> = {
   collection: "folder",
   database: "database",
   table: "table",
diff --git a/frontend/src/metabase/query_builder/components/view/ViewHeader/components/QuestionDataSource/utils.js b/frontend/src/metabase/query_builder/components/view/ViewHeader/components/QuestionDataSource/utils.js
index 9088da66f4a25a426975ee96b61ca45ed21558db..227175fb8f877d67e67b960f91a1acaba7a22cd0 100644
--- a/frontend/src/metabase/query_builder/components/view/ViewHeader/components/QuestionDataSource/utils.js
+++ b/frontend/src/metabase/query_builder/components/view/ViewHeader/components/QuestionDataSource/utils.js
@@ -16,7 +16,12 @@ import { HeadBreadcrumbs } from "../HeaderBreadcrumbs";
 
 import { IconWrapper, TablesDivider } from "./QuestionDataSource.styled";
 
-export function getDataSourceParts({ question, subHead, isObjectDetail }) {
+export function getDataSourceParts({
+  question,
+  subHead,
+  isObjectDetail,
+  formatTableAsComponent = true,
+}) {
   if (!question) {
     return [];
   }
@@ -39,6 +44,7 @@ export function getDataSourceParts({ question, subHead, isObjectDetail }) {
       icon: !subHead ? "database" : undefined,
       name: database.displayName(),
       href: database.id >= 0 && Urls.browseDatabase(database),
+      model: "database",
     });
   }
 
@@ -49,6 +55,7 @@ export function getDataSourceParts({ question, subHead, isObjectDetail }) {
     const isBasedOnSavedQuestion = isVirtualCardId(table.id);
     if (!isBasedOnSavedQuestion) {
       parts.push({
+        model: "schema",
         name: table.schema_name,
         href: database.id >= 0 && Urls.browseSchema(table),
       });
@@ -81,14 +88,22 @@ export function getDataSourceParts({ question, subHead, isObjectDetail }) {
         }),
     ].filter(isNotNull);
 
-    parts.push(
+    const part = formatTableAsComponent ? (
       <QuestionTableBadges
         tables={allTables}
         subHead={subHead}
         hasLink={hasTableLink}
         isLast={!isObjectDetail}
-      />,
+      />
+    ) : (
+      {
+        name: table.displayName(),
+        href: hasTableLink ? getTableURL(table) : "",
+        model: table.type ?? "table",
+      }
     );
+
+    parts.push(part);
   }
 
   return parts.filter(part => isValidElement(part) || part.name || part.icon);
diff --git a/frontend/src/metabase/query_builder/components/view/ViewHeader/components/QuestionDataSource/utils.unit.spec.ts b/frontend/src/metabase/query_builder/components/view/ViewHeader/components/QuestionDataSource/utils.unit.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e57594e4090a1d7809403dbe643481f653d9b916
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/view/ViewHeader/components/QuestionDataSource/utils.unit.spec.ts
@@ -0,0 +1,88 @@
+import { isValidElement } from "react";
+
+import { createMockMetadata } from "__support__/metadata";
+import Question from "metabase-lib/v1/Question";
+import type { Card } from "metabase-types/api";
+import {
+  createMockCard,
+  createMockDatabase,
+  createMockTable,
+} from "metabase-types/api/mocks";
+import { createSampleDatabase } from "metabase-types/api/mocks/presets";
+
+import { getDataSourceParts } from "./utils";
+
+const MULTI_SCHEMA_DB_ID = 2;
+const MULTI_SCHEMA_TABLE1_ID = 100;
+const MULTI_SCHEMA_TABLE2_ID = 101;
+
+function getMetadata() {
+  return createMockMetadata({
+    databases: [
+      createSampleDatabase(),
+      createMockDatabase({
+        id: MULTI_SCHEMA_DB_ID,
+        tables: [
+          createMockTable({
+            id: MULTI_SCHEMA_TABLE1_ID,
+            db_id: MULTI_SCHEMA_DB_ID,
+            schema: "first_schema",
+          }),
+          createMockTable({
+            id: MULTI_SCHEMA_TABLE2_ID,
+            db_id: MULTI_SCHEMA_DB_ID,
+            schema: "second_schema",
+          }),
+        ],
+      }),
+    ],
+  });
+}
+
+const createMockQuestion = (opts?: Partial<Card>) =>
+  new Question(createMockCard(opts), getMetadata());
+
+/** These tests cover new logic in the getDataSourceParts utility that is not covered in QuestionDataSource.unit.spec.js */
+describe("getDataSourceParts", () => {
+  afterEach(() => {
+    jest.restoreAllMocks();
+  });
+  it("returns an array of Records if formatTableAsComponent is false", () => {
+    const parts = getDataSourceParts({
+      question: createMockQuestion(),
+      subHead: false,
+      isObjectDetail: true,
+      formatTableAsComponent: false,
+    });
+    expect(parts).toHaveLength(2);
+    const partsArray = parts as any[];
+    expect(partsArray[0]).toEqual({
+      icon: "database",
+      name: "Sample Database",
+      href: "/browse/databases/1-sample-database",
+      model: "database",
+    });
+    expect(partsArray[1].name).toEqual("Products");
+    expect(partsArray[1].model).toEqual("table");
+    expect(partsArray[1].href).toMatch(/^\/question#[a-zA-Z0-9]{50}/);
+    expect(Object.keys(partsArray[1])).toHaveLength(3);
+  });
+
+  it("returns an array with the table formatted as a component if formatTableAsComponent is true", () => {
+    const parts = getDataSourceParts({
+      question: createMockQuestion(),
+      subHead: false,
+      isObjectDetail: true,
+      formatTableAsComponent: true,
+    });
+    expect(parts).toHaveLength(2);
+    const partsArray = parts as any[];
+    expect(partsArray[0]).toEqual({
+      icon: "database",
+      name: "Sample Database",
+      href: "/browse/databases/1-sample-database",
+      model: "database",
+    });
+    expect(isValidElement(partsArray[1])).toBe(true);
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/QuestionDetails.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/QuestionDetails.tsx
index fca6c9a29e479b32cb6b9de1101d9abe960a4bcc..be1534814f9326ea667f983b2b5b33a94a9a8907 100644
--- a/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/QuestionDetails.tsx
+++ b/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/QuestionDetails.tsx
@@ -2,22 +2,19 @@ import cx from "classnames";
 import { useState } from "react";
 import { c, t } from "ttag";
 
-import { getTableUrl } from "metabase/browse/containers/TableBrowser/TableBrowser";
 import { getCollectionName } from "metabase/collections/utils";
 import { SidesheetCardSection } from "metabase/common/components/Sidesheet";
 import DateTime from "metabase/components/DateTime";
 import Link from "metabase/core/components/Link";
 import Styles from "metabase/css/core/index.css";
-import { useSelector } from "metabase/lib/redux";
 import * as Urls from "metabase/lib/urls";
 import { getUserName } from "metabase/lib/user";
-import { getMetadata } from "metabase/selectors/metadata";
 import { QuestionPublicLinkPopover } from "metabase/sharing/components/PublicLinkPopover";
 import { Box, Flex, FixedSizeIcon as Icon, Text } from "metabase/ui";
 import type Question from "metabase-lib/v1/Question";
-import type { Database } from "metabase-types/api";
 
 import SidebarStyles from "./QuestionInfoSidebar.module.css";
+import { QuestionSources } from "./components/QuestionSources";
 
 export const QuestionDetails = ({ question }: { question: Question }) => {
   const lastEditInfo = question.lastEditInfo();
@@ -74,52 +71,11 @@ export const QuestionDetails = ({ question }: { question: Question }) => {
         </Flex>
       </SidesheetCardSection>
       <SharingDisplay question={question} />
-      <SourceDisplay question={question} />
+      <QuestionSources question={question} />
     </>
   );
 };
 
-function SourceDisplay({ question }: { question: Question }) {
-  const sourceInfo = question.legacyQueryTable();
-  const metadata = useSelector(getMetadata);
-
-  if (!sourceInfo) {
-    return null;
-  }
-
-  const model = String(sourceInfo.id).includes("card__") ? "card" : "table";
-
-  const sourceUrl =
-    model === "card"
-      ? Urls.browseDatabase(sourceInfo.db as Database)
-      : getTableUrl(sourceInfo, metadata);
-
-  return (
-    <SidesheetCardSection title={t`Based on`}>
-      <Flex gap="sm" align="center">
-        {sourceInfo.db && (
-          <>
-            <Text>
-              <Link
-                to={`/browse/databases/${sourceInfo.db.id}`}
-                variant="brand"
-              >
-                {sourceInfo.db.name}
-              </Link>
-            </Text>
-            {"/"}
-          </>
-        )}
-        <Text>
-          <Link to={sourceUrl} variant="brand">
-            {sourceInfo?.display_name}
-          </Link>
-        </Text>
-      </Flex>
-    </SidesheetCardSection>
-  );
-}
-
 function SharingDisplay({ question }: { question: Question }) {
   const publicUUID = question.publicUUID();
   const embeddingEnabled = question._card.enable_embedding;
diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/QuestionSources.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/QuestionSources.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..7fa2bba3cc6e124639e32a663b56f15d165a56cf
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/QuestionSources.tsx
@@ -0,0 +1,56 @@
+import { Fragment, useMemo } from "react";
+import { c } from "ttag";
+
+import { SidesheetCardSection } from "metabase/common/components/Sidesheet";
+import Link from "metabase/core/components/Link";
+import { Flex, FixedSizeIcon as Icon } from "metabase/ui";
+import type Question from "metabase-lib/v1/Question";
+
+import { getDataSourceParts } from "../../../ViewHeader/components/QuestionDataSource/utils";
+
+import type { QuestionSource } from "./types";
+import { getIconPropsForSource } from "./utils";
+
+export const QuestionSources = ({ question }: { question: Question }) => {
+  const sources = getDataSourceParts({
+    question,
+    subHead: false,
+    isObjectDetail: true,
+    formatTableAsComponent: false,
+  }) as unknown as QuestionSource[];
+
+  const sourcesWithIcons: QuestionSource[] = useMemo(() => {
+    return sources.map(source => ({
+      ...source,
+      iconProps: getIconPropsForSource(source),
+    }));
+  }, [sources]);
+
+  if (!sources.length) {
+    return null;
+  }
+
+  const title = c(
+    "This is a heading that appears above the names of the database, table, and/or question that a question is based on -- the 'sources' for the question. Feel free to translate this heading as though it said 'Based on these sources', if you think that would make more sense in your language.",
+  ).t`Based on`;
+
+  return (
+    <SidesheetCardSection title={title}>
+      <Flex gap="sm" align="flex-start">
+        {sourcesWithIcons.map(({ href, name, iconProps }, index) => (
+          <Fragment key={`${href}-${name}`}>
+            <Link to={href} variant="brand">
+              <Flex gap="sm" lh="1.25rem" maw="20rem">
+                {iconProps ? (
+                  <Icon mt={2} c="text-dark" {...iconProps} />
+                ) : null}
+                {name}
+              </Flex>
+            </Link>
+            {index < sources.length - 1 && <Flex lh="1.25rem">{"/"}</Flex>}
+          </Fragment>
+        ))}
+      </Flex>
+    </SidesheetCardSection>
+  );
+};
diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/QuestionSources.unit.spec.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/QuestionSources.unit.spec.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..d12e56ed2bf0549c39171d8f665051a5fefbf803
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/QuestionSources.unit.spec.tsx
@@ -0,0 +1,145 @@
+import { Route } from "react-router";
+import _ from "underscore";
+
+import { mockSettings } from "__support__/settings";
+import { createMockEntitiesState } from "__support__/store";
+import { renderWithProviders, screen, within } from "__support__/ui";
+import { modelIconMap } from "metabase/lib/icon";
+import { checkNotNull } from "metabase/lib/types";
+import { getMetadata } from "metabase/selectors/metadata";
+import { convertSavedQuestionToVirtualTable } from "metabase-lib/v1/metadata/utils/saved-questions";
+import type { Card, NormalizedTable } from "metabase-types/api";
+import { createMockCard, createMockSettings } from "metabase-types/api/mocks";
+import { createSampleDatabase } from "metabase-types/api/mocks/presets";
+import { createMockState } from "metabase-types/store/mocks";
+
+import { QuestionSources } from "./QuestionSources";
+
+interface SetupOpts {
+  card?: Card;
+  sourceCard?: Card;
+}
+
+const setup = async ({
+  card = createMockCard(),
+  sourceCard,
+}: SetupOpts = {}) => {
+  const state = createMockState({
+    settings: mockSettings(createMockSettings()),
+    entities: createMockEntitiesState({
+      databases: [createSampleDatabase()],
+      questions: _.compact([card, sourceCard]),
+    }),
+  });
+
+  // 😫 all this is necessary to test a card as a question source
+  if (sourceCard) {
+    const virtualTable = convertSavedQuestionToVirtualTable(sourceCard);
+
+    state.entities = {
+      ...state.entities,
+      tables: {
+        ...(state.entities.tables as Record<number, NormalizedTable>),
+        [virtualTable.id]: virtualTable,
+      },
+      databases: {
+        [state.entities.databases[1].id]: {
+          ...state.entities.databases[1],
+          tables: [
+            ...(state.entities.databases[1].tables ?? []),
+            virtualTable.id,
+          ],
+        },
+      },
+    };
+  }
+
+  const metadata = getMetadata(state);
+  const question = checkNotNull(metadata.question(card.id));
+
+  return renderWithProviders(
+    <Route
+      path="/"
+      component={() => <QuestionSources question={question} />}
+    />,
+    {
+      withRouter: true,
+    },
+  );
+};
+
+describe("QuestionSources", () => {
+  it("should show table source information", async () => {
+    const card = createMockCard({
+      name: "Question",
+    });
+    setup({ card });
+    const databaseLink = await screen.findByRole("link", {
+      name: /Sample Database/i,
+    });
+    expect(
+      await within(databaseLink).findByLabelText("database icon"),
+    ).toBeInTheDocument();
+    expect(databaseLink).toHaveAttribute(
+      "href",
+      "/browse/databases/1-sample-database",
+    );
+    expect(screen.getByText("/")).toBeInTheDocument();
+    const tableLink = await screen.findByRole("link", { name: /Products/i });
+    expect(tableLink).toBeInTheDocument();
+    expect(
+      await within(tableLink).findByLabelText(`table icon`),
+    ).toBeInTheDocument();
+    expect(tableLink).toHaveAttribute(
+      "href",
+      expect.stringMatching(/^\/question#[a-zA-Z0-9]{20}/),
+    );
+  });
+
+  it("should show card source information", async () => {
+    const card = createMockCard({
+      name: "My Question",
+      dataset_query: {
+        type: "query",
+        database: 1,
+        query: {
+          "source-table": "card__2",
+        },
+      },
+    });
+
+    const sourceCard = createMockCard({
+      name: "My Source Question",
+      id: 2,
+    });
+
+    await setup({ card, sourceCard });
+
+    const databaseLink = await screen.findByRole("link", {
+      name: /Sample Database/i,
+    });
+
+    expect(
+      await within(databaseLink).findByLabelText("database icon"),
+    ).toBeInTheDocument();
+    expect(databaseLink).toHaveAttribute(
+      "href",
+      "/browse/databases/1-sample-database",
+    );
+
+    expect(screen.getByText("/")).toBeInTheDocument();
+
+    const questionLink = await screen.findByRole("link", {
+      name: /My Source Question/i,
+    });
+    expect(
+      await within(questionLink).findByLabelText(
+        `${modelIconMap["card"]} icon`,
+      ),
+    ).toBeInTheDocument();
+    expect(questionLink).toHaveAttribute(
+      "href",
+      "/question/2-my-source-question",
+    );
+  });
+});
diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/types.ts b/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3f42bc97578c4810cd874b357f4b8841e1dda8c1
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/types.ts
@@ -0,0 +1,8 @@
+import type { IconData } from "metabase/lib/icon";
+
+export interface QuestionSource {
+  href: string;
+  name: string;
+  model?: string;
+  iconProps?: IconData;
+}
diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/utils.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/utils.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..dd3b0538ca0d7fe2e4486486b135441387ab2335
--- /dev/null
+++ b/frontend/src/metabase/query_builder/components/view/sidebars/QuestionInfoSidebar/components/utils.tsx
@@ -0,0 +1,21 @@
+import { match } from "ts-pattern";
+
+import { type IconData, type IconModel, getIcon } from "metabase/lib/icon";
+
+import type { QuestionSource } from "./types";
+
+export const getIconPropsForSource = (
+  source: QuestionSource,
+): IconData | undefined => {
+  const iconModel: IconModel | undefined = match(source.model)
+    .with("question", () => "card" as const)
+    .with("model", () => "dataset" as const)
+    .with("database", () => "database" as const)
+
+    .with("metric", () => "metric" as const)
+    .with("schema", () => undefined)
+    .otherwise(() => "table" as const);
+
+  const iconProps = iconModel ? getIcon({ model: iconModel }) : undefined;
+  return iconProps;
+};