Alright, here’s an example. You can place your TOC in the sidebar or on the main page, anywhere you want. The only requirement is to use toc’s title()
, header()
and subheader()
methods to create your titles. Call toc.generate()
once you have displayed every title, at the very end of your app.
And just in case, make sure you don’t use user inputs to create your titles. I’m using unsafe_allow_html
, so if the end user can change titles somehow by anything he wants, he’d be able to execute code on your page.
Source code here
import streamlit as st
class Toc:
def __init__(self):
self._items = []
self._placeholder = None
def title(self, text):
self._markdown(text, "h1")
def header(self, text):
self._markdown(text, "h2", " " * 2)
def subheader(self, text):
self._markdown(text, "h3", " " * 4)
def placeholder(self, sidebar=False):
self._placeholder = st.sidebar.empty() if sidebar else st.empty()
def generate(self):
if self._placeholder:
self._placeholder.markdown("\n".join(self._items), unsafe_allow_html=True)
def _markdown(self, text, level, space=""):
key = "".join(filter(str.isalnum, text)).lower()
st.markdown(f"<{level} id='{key}'>{text}</{level}>", unsafe_allow_html=True)
self._items.append(f"{space}* <a href='#{key}'>{text}</a>")
toc = Toc()
st.title("Table of contents")
toc.placeholder()
toc.title("Title")
for a in range(10):
st.write("Blabla...")
toc.header("Header 1")
for a in range(10):
st.write("Blabla...")
toc.header("Header 2")
for a in range(10):
st.write("Blabla...")
toc.subheader("Subheader 1")
for a in range(10):
st.write("Blabla...")
toc.subheader("Subheader 2")
for a in range(10):
st.write("Blabla...")
toc.generate()