How to Add a Streamlit Editable Code Editor with Python Syntax Highlighting?

Hi Team!

Iā€™m working on a Streamlit app where I want to provide users with an editable code block box, similar to the existing Streamlit text box component, but with Python syntax highlighting. This would allow users to input and edit Python code within the app.

Iā€™ve looked at the Streamlit documentation and the available components, but I couldnā€™t find a direct solution for this specific use case. I would like to add a feature that not only lets users input Python code but also highlights the syntax for better readability.

Is there a recommended approach or a community-contributed solution for adding an editable code block box with syntax highlighting for Python code in Streamlit?

Any insights, tips, or guidance would be greatly appreciated. Thanks in advance for your help!

At least these are known to me, maybe there are even more.

Maybe also related:

I would recommend streamlit-code-editor if the look you are going for is like st.code but editable (so not exactly like st.text_area). By default, the code editor will return a response when the user presses Ctrl+Enter just like st.text_area.

Here is the current repo where you will find installation instructions and more:

Another option that hasnt been mentioned yet is the Monaco editor:

This is great. I went with ā€˜code editorā€™. Thanks yā€™all!

Oh also, quick tip if you just started exploring using the code editor. You will get a nicer experience if you set allow_reset=True and give it a fixed key (like say key="my_editor"). This will prevent the editor from being recreated everytime your script re-runs and also allowing you to change/update the arguments to the component.

Do note that if you do the recommended thing and set a fixed key, the response dictionary will persist (because the code editor is the same one that existed prior to re-run) and so the old response sticks around until a new one is sent and the way you can tell if a new response has just been sent is by the id in the dict. Each response has a unique id. Like, for example, each time you press Ctrl+Enter the id value will change. So you can just use this fact to make an if statement that checks if the response is new.

I think a cleaner way to avoid performing an action twice (or more) if the id has not changed is to encapsule the logic to perform in a handle_action function and cache it using the id value of the dict returned by code_editor.

@bouzidanas , what do you think about it?

A minimalistic example of what Iā€™m saying:

@st.cache_resource(hash_funcs={dict: lambda response: response.get("id")})
def handle_action(code_editor_repsonse):
    some_function(code_editor_repsonse)

code_editor_response = code_editor(
        code="qwe",
        buttons=[
            {
                "name": "Save",
                "feather": "Save",
                "hasText": True,
                "commands": ["save-state", ["response", "saved"]],
                "response": "saved",
            }
        ],
    )
handle_action(code_editor_response)

Hi @arielcv96

Nice! That does look cleaner than creating a session variable and adding an if statement to check whether or not id changed.

I will play around with that when I get some time hopefully soon.

I definitely think its a good idea to provide some more practical examples to the docs and your example could prove to be a good addition!