diff --git a/frontend/src/plugins/layout/OutlinePlugin.tsx b/frontend/src/plugins/layout/OutlinePlugin.tsx
new file mode 100644
index 00000000000..d025bd06e7a
--- /dev/null
+++ b/frontend/src/plugins/layout/OutlinePlugin.tsx
@@ -0,0 +1,69 @@
+/* Copyright 2024 Marimo. All rights reserved. */
+import { Provider, useAtomValue } from "jotai";
+import type { JSX } from "react";
+import { z } from "zod";
+import { notebookOutline } from "@/core/cells/cells";
+import { store } from "@/core/state/jotai";
+import { OutlineList } from "../../components/editor/chrome/panels/outline/floating-outline";
+import {
+ findOutlineElements,
+ useActiveOutline,
+} from "../../components/editor/chrome/panels/outline/useActiveOutline";
+import type {
+ IStatelessPlugin,
+ IStatelessPluginProps,
+} from "../stateless-plugin";
+
+interface Data {
+ label?: string;
+}
+
+const OutlineContent: React.FC<{ label?: string }> = ({ label }) => {
+ const { items } = useAtomValue(notebookOutline);
+ const headerElements = findOutlineElements(items);
+ const { activeHeaderId, activeOccurrences } =
+ useActiveOutline(headerElements);
+
+ if (items.length === 0) {
+ return (
+
+ No outline found. Add markdown headings to your notebook to create an
+ outline.
+
+ );
+ }
+
+ return (
+
+ {label && (
+
+ {label}
+
+ )}
+
+
+ );
+};
+
+export class OutlinePlugin implements IStatelessPlugin {
+ tagName = "marimo-outline";
+
+ validator = z.object({
+ label: z.string().optional(),
+ });
+
+ render(props: IStatelessPluginProps): JSX.Element {
+ const { label } = props.data;
+
+ return (
+
+
+
+ );
+ }
+}
diff --git a/frontend/src/plugins/plugins.ts b/frontend/src/plugins/plugins.ts
index ab0729b6cff..53028d50ed0 100644
--- a/frontend/src/plugins/plugins.ts
+++ b/frontend/src/plugins/plugins.ts
@@ -45,6 +45,7 @@ import { JsonOutputPlugin } from "./layout/JsonOutputPlugin";
import { LazyPlugin } from "./layout/LazyPlugin";
import { MimeRendererPlugin } from "./layout/MimeRenderPlugin";
import { MermaidPlugin } from "./layout/mermaid/MermaidPlugin";
+import { OutlinePlugin } from "./layout/OutlinePlugin";
import { ProgressPlugin } from "./layout/ProgressPlugin";
import { RoutesPlugin } from "./layout/RoutesPlugin";
import { StatPlugin } from "./layout/StatPlugin";
@@ -99,6 +100,7 @@ const LAYOUT_PLUGINS: Array> = [
new MimeRendererPlugin(),
new MermaidPlugin(),
new NavigationMenuPlugin(),
+ new OutlinePlugin(),
new ProgressPlugin(),
new RoutesPlugin(),
new StatPlugin(),
diff --git a/marimo/__init__.py b/marimo/__init__.py
index 22d097e8ddf..0e96577022b 100644
--- a/marimo/__init__.py
+++ b/marimo/__init__.py
@@ -60,6 +60,7 @@
"nav_menu",
"notebook_dir",
"notebook_location",
+ "outline",
"output",
"pdf",
"persistent_cache",
@@ -98,6 +99,7 @@
from marimo._output.hypertext import Html
from marimo._output.justify import center, left, right
from marimo._output.md import latex, md
+from marimo._output.outline import outline
from marimo._output.show_code import show_code
from marimo._plugins import ui
from marimo._plugins.stateless import mpl, status
diff --git a/marimo/_output/outline.py b/marimo/_output/outline.py
new file mode 100644
index 00000000000..f0148bda0cd
--- /dev/null
+++ b/marimo/_output/outline.py
@@ -0,0 +1,38 @@
+# Copyright 2024 Marimo. All rights reserved.
+from __future__ import annotations
+
+from marimo._output.hypertext import Html
+from marimo._output.rich_help import mddoc
+from marimo._plugins.core.web_component import build_stateless_plugin
+
+
+@mddoc
+def outline(*, label: str = "") -> Html:
+ """Display a table of contents outline showing all markdown headers in the notebook.
+
+ The outline automatically extracts all markdown headers from executed cells
+ and displays them in a hierarchical structure with clickable navigation.
+
+ Examples:
+ Basic outline:
+ ```python
+ mo.outline()
+ ```
+
+ With custom label:
+ ```python
+ mo.outline(label="Table of Contents")
+ ```
+
+ Args:
+ label (str, optional): A descriptive label for the outline. Defaults to "".
+
+ Returns:
+ Html: An HTML object that renders the outline component.
+ """
+ return Html(
+ build_stateless_plugin(
+ component_name="marimo-outline",
+ args={"label": label},
+ )
+ )
diff --git a/tests/snapshots/api.txt b/tests/snapshots/api.txt
index 629ebce650d..fbfae98788a 100644
--- a/tests/snapshots/api.txt
+++ b/tests/snapshots/api.txt
@@ -51,6 +51,7 @@ mpl
nav_menu
notebook_dir
notebook_location
+outline
output
append
clear