Microsoft Clarity tracking analytics not working properly

Summary

I am trying to collect some analytics data on my streamlit app that’s hosted on streamlit cloud. I tried adding the tracking code for Microsoft Clarity to the head file. I see some tracking metrics pop up but the numbers looks quite off.

Steps to reproduce

Code snippet:

import streamlit.components.v1 as components

components.html("""
<script type="text/javascript">
    (function(c,l,a,r,i,t,y){
        c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
        t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
        y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
    })(window, document, "clarity", "script", "g665b0hqie");
</script>
    """)

As part of the instructions, I had to add the script tag in the file.

If applicable, please provide the steps we should take to reproduce the error or specified behavior.

Expected behavior:
I am expecting more actual metrics popping up. Example, I know users from other countries used to login to the app, but it doesn’t show up here. Also the heatmaps tab doesn’t capture properly the user journey in the website.

Actual behavior:

Additional information

Should I be adding the tracking code in a different approach?

1 Like

I have the same issue.

1 Like

So do I

2 Likes

Anyone from streamlit can help on this please??

1 Like

Hey all,
Are you still experiencing tracking inconsistency?
Thank you

1 Like

I’ve inserted the Clarity script into our Streamlit app but the Clarity recordings do not show any screens or even clicks. All it shows is a gray line.

In order to include the Clarity script in the head tag, I inserted a javascript function to find the head tag and append a script element containing the Clarity snippet.

document.addEventListener("DOMContentLoaded", function (event) {
    let headElements = document.getElementsByTagName("head");
    let headElement = headElements[0]
    let textNode = document.createTextNode("inserting clarity here")
    let clarityScript = document.createElement("script");
    clarityScript.type = "text/javascript"
    let code = `(function(c,l,a,r,i,t,y){
        c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
        t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
        y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
    })(window, document, "clarity", "script", "abc123456789");`
    clarityScript.appendChild(document.createTextNode(code));
    headElement.appendChild(textNode);
    headElement.appendChild(clarityScript);
});

I used streamlit_javascript to run this javascript at the start of our app:

import streamlit_javascript
...
with open('static_resources/append_clarity.js') as f:
                st_javascript(f"{f.read()}")

Although I used this script to find the first head tag, the rendered page has a head tag above it.

Here’s an abbreviated outline of the html document…

<iframe>
<html>
<head>    <---- this is first head tag (but not found by my script)
<body>
<div>
...
<div>
<html>  
<head>     <--- this is the head element where my script inserts clarity
2 Likes

Hey all - having the same issue as aforementioned and wondering if any one has had luck figuring out a solution :crossed_fingers:

1 Like

@agni
How did you add it to the head file? I have added in the main.py file with st.markdown and the clarity script tag code. It is not working at all
How can I make it working? Please help me with this as soon as possible.

@Harshk97

I applied the following approach:

components.html("""
<script type="text/javascript">
    (function(c,l,a,r,i,t,y){
        c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
        t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
        y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
    })(window, document, "clarity", "script", "g665b0hqie");
</script>
    """)

I pasted the javascript code in the components.html part

Hey all,

I have manage to find a solution to add the clarity script to the head of each page of streamlit app. So my solution was to create a add_track.py script with the following information:

from bs4 import BeautifulSoup
import pathlib
import shutil
import streamlit as st


CLARITY_ID = "clarity_analytics"
CLARITY_SCRIPT = """
<script type="text/javascript">
{your clarity script}
</script>
"""

def inject_ga():
    
    index_path = pathlib.Path(st.__file__).parent / "static" / "index.html"
    soup = BeautifulSoup(index_path.read_text(), features="html.parser")
    if not soup.find(id=CLARITY_ID): 
        bck_index = index_path.with_suffix('.bck')
        if bck_index.exists():
            shutil.copy(bck_index, index_path)  
        else:
            shutil.copy(index_path, bck_index)
        html = str(soup)
        new_html = html.replace('<head>', '<head>\n' + CLARITY_SCRIPT)
        index_path.write_text(new_html)

inject_ga()

Then when I run the app in my docker file I need to run this code and the one that runs the streamlit, something like this:

poetry run python app/add_track.py && poetry run streamlit run app/streamlit_app.py

I am using poetry btw :slight_smile:

Now, I am facing another issue when I am using streamlit platform to deploy the app I am unable to run the command line above. I can only run it when deploying with docker using a service like heroku

1 Like

Kudos to franciscodelca for the trick to insert the Clarity script in the head of each Streamlit page.

It wasn’t immediately obvious what it was doing. His script finds the index.html in the Streamlit virtual environment files.

For example:
your_home_path/.venv/lib/python3.10/site-packages/streamlit/static/index.html

And then it checks if there’s a script tag with an id=“clarity_analytics”.

If not, it adds your clarity script to the html file and saves it.

This works because this index.html file is the parent file used by Streamlit for the entire Streamlit page.

Brilliant approach!

Now the trick is how to run it for your deployed app?

In our case, we’re deploying the app on Azure using an Azure DevOps pipeline.

To get this to work, I added a script step to our pipeline yaml config file which:
– activates the virtual environment
– runs the Python script to inject the Clarity script tag (config_clarity.py)
– displays the modified index.html file (for confirmation)

Here’s the script I’m using for Azure DevOps…

- script: |
        source antenv/bin/activate
        python config_clarity.py
        echo cd antenv/lib/python3.10/site-packages/streamlit/static
        cd antenv/lib/python3.10/site-packages/streamlit/static
        echo ls -la
        ls -la
        echo cat index.html
        cat index.html

Clarity is now working for our Streamlit app. The only issue so far: a custom component doesn’t appear…strange.

2 Likes

I modified the ‘add_track.py’ code and ran ‘add_track.py’ before executing ‘stramlit run app.py’.
After the program started, I found the injected clarity script in the source code of localhost:8051.
But in the browser’s debug page, there is no clarity report request in the Network Tab. Only GET ‘https://www.clarity.ms/tag/olm4y3l6w3’ is ‘200 OK’, and GET ‘https://www.clarity.ms/s/0.7.49/clarity.js’ is ‘429 Too Many Requests’.
And no report information can be found in clarity.microsoft.com.

What may be the reason?

from bs4 import BeautifulSoup
import pathlib
import shutil
import streamlit as st

CLARITY_SCRIPT = """
<script type="text/javascript">
    (function(c,l,a,r,i,t,y){
        c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
        t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
        y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
    })(window, document, "clarity", "script", "<--my-clarity-tag-->");
</script>
"""
INJECTION_COMMENT = "<!-- Clarity script injected -->"


def inject_ga():
    index_path = pathlib.Path(st.__file__).parent.joinpath('static', 'index.html')
    soup = BeautifulSoup(index_path.read_text(), features="html.parser")
    if INJECTION_COMMENT not in str(soup):
        bck_index = index_path.with_suffix('.bck')
        if bck_index.exists():
            shutil.copy(bck_index, index_path)
        else:
            shutil.copy(index_path, bck_index)
        html = str(soup)
        new_html = html.replace('<head>', '<head>\n' + CLARITY_SCRIPT + INJECTION_COMMENT)
        index_path.write_text(new_html)
        print('Clarity inject Done\n')
    else:
        print('Clarity injected. Skipping.')


if __name__ == '__main__':
    inject_ga()