Table of contents widget

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()

streamlit-toc-2020-06-16-19-06-03.webm

7 Likes