Incorrect question and radio text rendered on iOS Safari (text cut off / mixed with unrelated words)

Hi all! :slight_smile:

I’m developing a quiz-style application in Streamlit that displays a question and multiple answers using st.radio inside a st.form.

On iOS Safari (iPhone), the UI renders incorrect text, even though the underlying data is correct.

What I see

  • Visually, the layout looks normal (no blur, no freezing, no spacing issues).

  • However, the question text and radio button labels are wrong:

    • Text appears cut off

    • Some words look like they are taken from other questions or answers

    • In some cases, parts of the text appear to be mixed or overwritten

  • The issue affects both:

    • the question text rendered via st.markdown

    • the radio button labels rendered via st.radio

  • This happens only on iOS Safari.

  • Desktop browsers (Chrome, Firefox) display the correct text, and the issue does not occur on Android..

Screenshots from iPhone clearly show that:

  • The displayed text does not match the actual question/answers

  • Some words appear to come from a different question or from nowhere obvious

Important: backend data is correct

  • In application logs, I see the correct question text and answer choices.

  • Debug prints confirm that:

    • question.text has the expected value

    • question.shuffled_choices contains the correct answer strings

  • The mismatch exists only in what is rendered on the screen, not in Python state.

Expected behavior

  • The text displayed in the browser should exactly match the values logged in Python.

  • Question text and radio labels should never contain fragments from other questions.

Streamlit version: 1.51.0
Python: 3.12.3
Ubuntu 24.04.3 LTS

requirements.txt:

Flask~=3.1.2
Flask-SQLAlchemy==3.1.1
Werkzeug~=3.1.3
SQLAlchemy~=2.0.44
PyJWT~=2.10.1
requests~=2.32.5
streamlit~=1.51.0
streamlit_js_eval~=0.1.7
dotenv~=0.9.9
python-dotenv~=1.2.1

screenshot 1 corresponding logs:
[2025-12-15 05:14:21,821] [DEBUG]: Response[200]: {“question_id”: 157, “text”: “Strzelania dynamiczne można prowadzić”, “difficulty”: “HARD”, “choices”: [{“id”: 625, “text”: “z wykorzystaniem osłon”}, {“id”: 626, “text”: “tylko w warunkach dobrej widoczności”}, {“id”: 627, “text”: “z bronią gotową do strzału”}, {“id”: 628, “text”: “wyłącznie na strzelnicach zamkniętych”}], “index”: 1, “total”: 571}

screenshot 1 corresponding logs:
[2025-12-15 05:15:16,677] [DEBUG]: Response[200]: {“question_id”: 50, “text”: “Osoby udzielające pomocy Policji to”, “difficulty”: “HARD”, “choices”: [{“id”: 197, “text”: “Osobowe źródła informacji, konsultanci oraz osoby wspierające”}, {“id”: 198, “text”: “Osobowe źródła informacji, konsultanci oraz osoby wspomagające”}, {“id”: 199, “text”: “Wyłącznie osobowe źródła informacji i konsultanci”}, {“id”: 200, “text”: “Wyłącznie osobowe źródła informacji”}], “index”: 4, “total”: 571}

I’d appreciate any help or pointers on this, as I’ve tried many approaches to solve it (unique keys, blurring, clearing cache, etc.), and nothing has helped :confused:

With a deesktop browser, I would use the developer tools to check the HTML. But I don’t know if you can do that with iOS Safari.

The problem is that I do not own an iPhone. My friend checked this for me, but he is not a technical person, and I’m afraid he won’t be able to help me with this. Also, I’m not sure whether this can be checked on a mobile phone.

Update:

Root cause:
The backend was returning correct data.
The issue was client-side rendering, not Safari specifically.

Modern browsers automatically process natural-language text (language detection, text shaping, optimizations). When dynamic text is frequently re-rendered, this can sometimes lead to corrupted or mixed text (missing words, fragments from previous renders).

This looks like caching or state bugs, but it’s actually browser text normalization.

Solution:
Render the text as literal content, not prose.

Using <code> or st.code() works because browsers do not apply linguistic processing to code blocks, so the text is rendered exactly as received.

If you encounter a similar issue with corrupted or mixed text, try rendering it with st.code() instead of st.write(), as code blocks disable browser text processing and render the content verbatim.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.