diff --git a/src/mkdocs_table_reader_plugin/markdown.py b/src/mkdocs_table_reader_plugin/markdown.py
index 6fb6e88..5460d1a 100644
--- a/src/mkdocs_table_reader_plugin/markdown.py
+++ b/src/mkdocs_table_reader_plugin/markdown.py
@@ -65,6 +65,13 @@ def escape(value):
# Escape a copy, so that a DataFrame passed in by a macros user is left alone
df = df.map(escape)
df.columns = [escape(c) for c in df.columns]
+ if markdown_kwargs["index"] and not isinstance(df.index, pd.MultiIndex):
+ df.index = pd.Index(
+ [escape(value) for value in df.index],
+ name=escape(df.index.name),
+ dtype=object,
+ tupleize_cols=False,
+ )
return df.to_markdown(**markdown_kwargs)
diff --git a/tests/test_markdown.py b/tests/test_markdown.py
index e649c66..59c8024 100644
--- a/tests/test_markdown.py
+++ b/tests/test_markdown.py
@@ -62,6 +62,76 @@ def test_convert_to_md_table_multiline_other_tablefmt():
assert "But not always" in md
+@pytest.mark.parametrize("tablefmt", ["pipe", "github"])
+@pytest.mark.parametrize("newline", ["\n", "\r\n", "\r"])
+def test_convert_to_md_table_index(tablefmt, newline):
+ df = pd.DataFrame(
+ {"value": [1, 2]},
+ index=pd.Index(["a|b", f"two{newline}lines"], name=f"row|{newline}name"),
+ )
+ original = df.copy(deep=True)
+
+ md = convert_to_md_table(df, index=True, tablefmt=tablefmt)
+
+ assert "a\\|b" in md
+ assert "two
lines" in md
+ assert "row\\|
name" in md
+ assert len(md.splitlines()) == 4
+ pd.testing.assert_frame_equal(df, original)
+ assert convert_to_md_table(df, index=True, tablefmt=tablefmt) == md
+
+
+@pytest.mark.parametrize("tablefmt", ["pipe", "github"])
+def test_convert_to_md_table_index_preescaped_pipes(tablefmt):
+ df = pd.DataFrame({"value": [1]}, index=pd.Index([r"a\|b"], name=r"row\|name"))
+
+ md = convert_to_md_table(df, index=True, tablefmt=tablefmt)
+
+ assert r"a\|b" in md
+ assert r"row\|name" in md
+ assert r"a\\|b" not in md
+ assert r"row\\|name" not in md
+
+
+def test_convert_to_md_table_index_grid():
+ df = pd.DataFrame({"value": [1]}, index=pd.Index(["a|b\ntwo"], name="row|name"))
+
+ md = convert_to_md_table(df, index=True, tablefmt="grid")
+
+ assert "
" not in md
+ assert r"a\|b" in md
+ assert "two" in md
+ assert r"row\|name" in md
+
+
+@pytest.mark.parametrize(
+ "index",
+ [
+ pd.Index([1, 2], name="row"),
+ pd.Index([1.25, 2.5], name="row"),
+ pd.date_range("2026-01-01", periods=2, name="row"),
+ pd.CategoricalIndex(["a", "b"], name="row"),
+ pd.Index(["a", None], dtype=object, name="row"),
+ pd.Index([(1, "a"), (2, "b")], name="row", tupleize_cols=False),
+ pd.MultiIndex.from_tuples([(1, "a"), (2, "b")], names=["number", "letter"]),
+ ],
+)
+def test_convert_to_md_table_index_types(index):
+ df = pd.DataFrame({"value": [1, 2]}, index=index)
+ original = df.copy(deep=True)
+
+ assert convert_to_md_table(df, index=True, floatfmt=".1f") == df.to_markdown(index=True, floatfmt=".1f")
+ pd.testing.assert_frame_equal(df, original)
+
+
+def test_convert_to_md_table_hidden_index():
+ df = pd.DataFrame({"value": [1]}, index=pd.Index(["a|b\ntwo"], name="row|name"))
+ original = df.copy(deep=True)
+
+ assert convert_to_md_table(df, index=False) == df.to_markdown(index=False)
+ pd.testing.assert_frame_equal(df, original)
+
+
def test_add_indentation():
"""
The filter used with mkdocs-macros-plugin, which strips indentation itself.