diff --git a/docs/embedding/sdk/questions.md b/docs/embedding/sdk/questions.md
index 43b1f722ea16fc6c6031f3e198ca0a02f6ea04cc..1e5b5f0e5d14a3e2c6e844aa9794f4e7c67892ef 100644
--- a/docs/embedding/sdk/questions.md
+++ b/docs/embedding/sdk/questions.md
@@ -69,19 +69,18 @@ export default function App() {
 ## Question props
 
 | Prop                  | Type                                                                 | Description                                                                                                                                                                                                                                                                                                        |
-|-----------------------|----------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| --------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
 | questionId            | number or string                                                     | (required) The ID of the question. This is either:<br>- The numerical ID when accessing a question link, e.g., `http://localhost:3000/question/1-my-question` where the ID is `1`.<br>- The `entity_id` key of the question object. You can find a question's entity ID in the info panel when viewing a question. |
 | plugins               | `{ mapQuestionClickActions: Function }` or null                      | Additional mapper function to override or add drill-down menu.                                                                                                                                                                                                                                                     |
 | height                | number or string                                                     | (optional) A number or string specifying a CSS size value that specifies the height of the component                                                                                                                                                                                                               |
 | entityTypeFilter      | string array; options include "table", "question", "model", "metric" | (optional) An array that specifies which entity types are available in the data picker                                                                                                                                                                                                                             |
 | isSaveEnabled         | boolean                                                              | (optional) Whether people can save the question.                                                                                                                                                                                                                                                                   |
 | withResetButton       | boolean                                                              | (optional, default: `true`) Determines whether a reset button is displayed. Only relevant when using the default layout                                                                                                                                                                                            |
-| withTitle             | boolean                                                              | (optional, default: `false`) Determines whether the question title is displayed. Only relevant when using the default layout.                                                                                                                                                                                      |
-| customTitle           | string or undefined                                                  | (optional) Allows a custom title to be displayed instead of the default question title. Only relevant when using the default layout.                                                                                                                                                                               |
 | withChartTypeSelector | boolean                                                              | (optional, default: `true`) Determines whether the chart type selector is shown. Only relevant when using the default layout.                                                                                                                                                                                      |
+| title                 | boolean or string or `ReactNode` or `() => ReactNode`                | (optional) Determines whether the question title is displayed, and allows a custom title to be displayed instead of the default question title. Shown by default. Only Only applicable to interactive questions when using the default layout.                                                                     |
 | onBeforeSave          | `() => void`                                                         | (optional) A callback function that triggers before saving. Only relevant when `isSaveEnabled = true`.                                                                                                                                                                                                             |
 | onSave                | `() => void`                                                         | (optional) A callback function that triggers when a user saves the question. Only relevant when `isSaveEnabled = true`.                                                                                                                                                                                            |
-| saveToCollectionId    | number                                                               | (optional) The target collection to save the question to. This will hide the collection picker from the save modal. Only applicable to static questions.                                                                                                                                                           |
+| saveToCollectionId    | number                                                               | (optional) The target collection to save the question to. This will hide the collection picker from the save modal. Only applicable to interactive questions.                                                                                                                                                      |
 
 ## Customizing interactive questions
 
diff --git a/enterprise/frontend/src/embedding-sdk/components/private/InteractiveAdHocQuestion.tsx b/enterprise/frontend/src/embedding-sdk/components/private/InteractiveAdHocQuestion.tsx
index 9840058a490dc189d069cd3dd5e602981b668ff9..5b626e9692c9de612aaa3f11c2b5b58528beb523 100644
--- a/enterprise/frontend/src/embedding-sdk/components/private/InteractiveAdHocQuestion.tsx
+++ b/enterprise/frontend/src/embedding-sdk/components/private/InteractiveAdHocQuestion.tsx
@@ -1,6 +1,7 @@
 import { type ReactNode, useMemo } from "react";
 
 import type { SdkPluginsConfig } from "embedding-sdk";
+import type { SdkQuestionTitleProps } from "embedding-sdk/types/question";
 
 import {
   InteractiveQuestionProviderWithLocation,
@@ -11,8 +12,7 @@ import { InteractiveQuestionResult } from "./InteractiveQuestionResult";
 interface InteractiveAdHocQuestionProps {
   questionPath: string; // route path to load a question, e.g. /question/140-best-selling-products - for saved, or /question/xxxxxxx for ad-hoc encoded question config
   onNavigateBack: () => void;
-
-  withTitle?: boolean;
+  title: SdkQuestionTitleProps;
   height?: number;
   plugins?: SdkPluginsConfig;
   children?: ReactNode;
@@ -21,7 +21,7 @@ interface InteractiveAdHocQuestionProps {
 export const InteractiveAdHocQuestion = ({
   questionPath,
   onNavigateBack,
-  withTitle = true,
+  title = true,
   height,
   plugins,
   children,
@@ -41,7 +41,7 @@ export const InteractiveAdHocQuestion = ({
       {children ?? (
         <InteractiveQuestionResult
           height={height}
-          withTitle={withTitle}
+          title={title}
           withChartTypeSelector
         />
       )}
diff --git a/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestionResult/InteractiveQuestionResult.tsx b/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestionResult/InteractiveQuestionResult.tsx
index d191b8242b7015286253a9507dda5db4c3f1df1d..5d73142d72eb5d597b57446adc7220962ee56d88 100644
--- a/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestionResult/InteractiveQuestionResult.tsx
+++ b/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestionResult/InteractiveQuestionResult.tsx
@@ -1,6 +1,6 @@
 import { useDisclosure } from "@mantine/hooks";
 import cx from "classnames";
-import { type ReactElement, type ReactNode, useState } from "react";
+import { type ReactElement, type ReactNode, useMemo, useState } from "react";
 import { match } from "ts-pattern";
 import { t } from "ttag";
 
@@ -8,6 +8,7 @@ import {
   SdkError,
   SdkLoader,
 } from "embedding-sdk/components/private/PublicComponentWrapper";
+import type { SdkQuestionTitleProps } from "embedding-sdk/types/question";
 import { SaveQuestionModal } from "metabase/containers/SaveQuestionModal";
 import { Box, Button, Group, Icon } from "metabase/ui";
 
@@ -21,9 +22,8 @@ import { useInteractiveQuestionContext } from "../InteractiveQuestion/context";
 import InteractiveQuestionS from "./InteractiveQuestionResult.module.css";
 
 export interface InteractiveQuestionResultProps {
+  title?: SdkQuestionTitleProps;
   withResetButton?: boolean;
-  withTitle?: boolean;
-  customTitle?: ReactNode;
   withChartTypeSelector?: boolean;
 }
 
@@ -55,8 +55,7 @@ export const InteractiveQuestionResult = ({
   width,
   className,
   style,
-  withTitle,
-  customTitle,
+  title,
   withResetButton,
   withChartTypeSelector,
 }: InteractiveQuestionResultProps & FlexibleSizeProps): ReactElement => {
@@ -83,6 +82,25 @@ export const InteractiveQuestionResult = ({
   // When visualizing a question for the first time, there is no query result yet.
   const isQueryResultLoading = question && !queryResults;
 
+  const questionTitleElement: ReactNode = useMemo(() => {
+    if (title === false) {
+      return null;
+    }
+
+    if (title === undefined || title === true) {
+      return <InteractiveQuestion.Title />;
+    }
+
+    if (typeof title === "function") {
+      const CustomTitle = title;
+
+      // TODO: pass in question={question} once we have the public-facing question type (metabase#50487)
+      return <CustomTitle />;
+    }
+
+    return title;
+  }, [title]);
+
   if (isQuestionLoading || isQueryResultLoading) {
     return <SdkLoader />;
   }
@@ -100,7 +118,7 @@ export const InteractiveQuestionResult = ({
     >
       <Group className={InteractiveQuestionS.TopBar} position="apart" p="md">
         <InteractiveQuestion.BackButton />
-        {withTitle && (customTitle ?? <InteractiveQuestion.Title />)}
+        {questionTitleElement}
         <Group spacing="xs">
           {withResetButton && <InteractiveQuestion.ResetButton />}
           <InteractiveQuestion.FilterButton
diff --git a/enterprise/frontend/src/embedding-sdk/components/public/InteractiveDashboard/EditableDashboard.tsx b/enterprise/frontend/src/embedding-sdk/components/public/InteractiveDashboard/EditableDashboard.tsx
index 7b82ac13865b37b543c805da9f16190ae81afdaf..bb00415bdafb9c3600bf44baf511655392693068 100644
--- a/enterprise/frontend/src/embedding-sdk/components/public/InteractiveDashboard/EditableDashboard.tsx
+++ b/enterprise/frontend/src/embedding-sdk/components/public/InteractiveDashboard/EditableDashboard.tsx
@@ -72,7 +72,7 @@ export const EditableDashboard = ({
       {adhocQuestionUrl ? (
         <InteractiveAdHocQuestion
           questionPath={adhocQuestionUrl}
-          withTitle
+          title={true}
           height={drillThroughQuestionHeight}
           plugins={plugins}
           onNavigateBack={onNavigateBackToDashboard}
diff --git a/enterprise/frontend/src/embedding-sdk/components/public/InteractiveDashboard/InteractiveDashboard.tsx b/enterprise/frontend/src/embedding-sdk/components/public/InteractiveDashboard/InteractiveDashboard.tsx
index aeebc991e4e1ecdda3d673e3edd3ae7eb9ff5736..0b555d8f32fc0f0e0eae98901a2f1e8a85d15dd0 100644
--- a/enterprise/frontend/src/embedding-sdk/components/public/InteractiveDashboard/InteractiveDashboard.tsx
+++ b/enterprise/frontend/src/embedding-sdk/components/public/InteractiveDashboard/InteractiveDashboard.tsx
@@ -85,7 +85,7 @@ const InteractiveDashboardInner = ({
       {adhocQuestionUrl ? (
         <InteractiveAdHocQuestion
           questionPath={adhocQuestionUrl}
-          withTitle={withTitle}
+          title={withTitle}
           height={drillThroughQuestionHeight}
           plugins={plugins}
           onNavigateBack={onNavigateBackToDashboard}
diff --git a/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.tsx b/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.tsx
index b6e9ca90cc3d6f3ef5e5a0b00f8d014732e54b2f..4bf5229eda96f359a4d3d765b660500db886279b 100644
--- a/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.tsx
+++ b/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.tsx
@@ -46,8 +46,7 @@ export type InteractiveQuestionProps = PropsWithChildren<{
 export const _InteractiveQuestion = ({
   questionId,
   withResetButton = true,
-  withTitle = false,
-  customTitle,
+  title,
   plugins,
   height,
   width,
@@ -78,9 +77,8 @@ export const _InteractiveQuestion = ({
         width={width}
         className={className}
         style={style}
-        customTitle={customTitle}
+        title={title}
         withResetButton={withResetButton}
-        withTitle={withTitle}
         withChartTypeSelector={withChartTypeSelector}
       />
     )}
diff --git a/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.unit.spec.tsx b/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.unit.spec.tsx
index 6eb3a7c61bce57d8b5a3e7dfebe3fcd86ccdb703..a916d26f1948ac50477dc2d88b9f285c921955e7 100644
--- a/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.unit.spec.tsx
+++ b/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.unit.spec.tsx
@@ -18,6 +18,7 @@ import {
 import { InteractiveQuestionResult } from "embedding-sdk/components/private/InteractiveQuestionResult";
 import { createMockAuthProviderUriConfig } from "embedding-sdk/test/mocks/config";
 import { setupSdkState } from "embedding-sdk/test/server-mocks/sdk-init";
+import type { SdkQuestionTitleProps } from "embedding-sdk/types/question";
 import {
   createMockCard,
   createMockCardQueryMetadata,
@@ -53,23 +54,29 @@ const TEST_DATASET = createMockDataset({
 });
 
 // Provides a button to re-run the query
-function InteractiveQuestionCustomLayout() {
+function InteractiveQuestionCustomLayout({
+  title,
+}: {
+  title?: SdkQuestionTitleProps;
+}) {
   const { resetQuestion } = useInteractiveQuestionContext();
 
   return (
     <div>
       <button onClick={resetQuestion}>Run Query</button>
-      <InteractiveQuestionResult withTitle />
+      <InteractiveQuestionResult title={title} />
     </div>
   );
 }
 
 const setup = ({
   isValidCard = true,
+  title,
   withCustomLayout = false,
   withChartTypeSelector = false,
 }: {
   isValidCard?: boolean;
+  title?: SdkQuestionTitleProps;
   withCustomLayout?: boolean;
   withChartTypeSelector?: boolean;
 } = {}) => {
@@ -77,7 +84,7 @@ const setup = ({
     currentUser: TEST_USER,
   });
 
-  const TEST_CARD = createMockCard();
+  const TEST_CARD = createMockCard({ name: "My Question" });
   if (isValidCard) {
     setupCardEndpoints(TEST_CARD);
     setupCardQueryMetadataEndpoint(
@@ -99,6 +106,7 @@ const setup = ({
   return renderWithProviders(
     <InteractiveQuestion
       questionId={TEST_CARD.id}
+      title={title}
       withChartTypeSelector={withChartTypeSelector}
     >
       {withCustomLayout ? <InteractiveQuestionCustomLayout /> : undefined}
@@ -180,6 +188,35 @@ describe("InteractiveQuestion", () => {
     expect(screen.getByText("Question not found")).toBeInTheDocument();
   });
 
+  it.each([
+    // shows the question title by default
+    [undefined, "My Question"],
+
+    // hides the question title when title={false}
+    [false, null],
+
+    // shows the default question title when title={true}
+    [true, "My Question"],
+
+    // customizes the question title via strings
+    ["Foo Bar", "Foo Bar"],
+
+    // customizes the question title via React elements
+    [<h1 key="foo">Foo Bar</h1>, "Foo Bar"],
+
+    // customizes the question title via React components.
+    [() => <h1>Foo Bar</h1>, "Foo Bar"],
+  ])(
+    "shows the question title according to the title prop",
+    async (titleProp, expectedTitle) => {
+      setup({ title: titleProp });
+      await waitForLoaderToBeRemoved();
+
+      const element = screen.queryByText(expectedTitle ?? "My Question");
+      expect(element?.textContent ?? null).toBe(expectedTitle);
+    },
+  );
+
   it("should show a chart type selector button if withChartTypeSelector is true", async () => {
     setup({ withChartTypeSelector: true });
     await waitForLoaderToBeRemoved();
diff --git a/enterprise/frontend/src/embedding-sdk/types/question.ts b/enterprise/frontend/src/embedding-sdk/types/question.ts
index 984c86528fcb91bf3ed76f231e5108c56692d2c3..2c8ee7c118a9b8660b9e4c6a3d3fd6cf8e117c6c 100644
--- a/enterprise/frontend/src/embedding-sdk/types/question.ts
+++ b/enterprise/frontend/src/embedding-sdk/types/question.ts
@@ -1,3 +1,5 @@
+import type { ReactNode } from "react";
+
 import type { Deferred } from "metabase/lib/promise";
 import type { QueryParams } from "metabase/query_builder/actions";
 import type { ObjectId } from "metabase/visualizations/components/ObjectDetail/types";
@@ -23,3 +25,10 @@ export interface NavigateToNewCardParams {
   objectId: ObjectId;
   cancelDeferred?: Deferred;
 }
+
+export type SdkQuestionTitleProps =
+  | boolean
+  | undefined
+  | ReactNode
+  // TODO: turn this into (question: Question) => ReactNode once we have the public-facing question type (metabase#50487)
+  | (() => ReactNode);