How to use MathJax with Dash AG Grid

Those work really well, but I’m trying to get this to work inside my Dash AG Grid table. The dcc.Markdown with the mathjax=True parameter works great, but it’s not what I need. Is there a way to format headers and data in Dash AG Grids using Mathjax? Or is that not possible yet?

HI @gdillon

Yes, you can use dcc.Markdown as a custom component in dash-ag-grid. Here’s an example to get you started - it’s used in the tooltip, but it can be used to format data and headers as well.

See this page:

Here’s how to define the component

Put the following in the dashAgGridComponentFunctions.js file in the assets folder


var dagcomponentfuncs = window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {};


dagcomponentfuncs.CustomTooltipMarkdown = function (props) {
    return React.createElement(
        window.dash_core_components.Markdown, {
            children: props.value,
            className: props.className,
            style: props.style,
            dangerously_allow_html : props.dangerously_allow_html | false,
            link_target: props.link_target,
            mathjax: props.mathjax
        })
};

If you create a minimal example with some data, I can help with applying the component if you like :slight_smile:

I would love your help applying the component! Here is some random example data:

Formula Inventor Symbols Year \alpha
E=mc^2 Einstein E,m,c 1905 0.05
y=mx+b Salmon y,m,x,b 0.10
x=\frac{-b \pm \sqrt{b^2 - 4ac}}{2a} Brahmagupta x,a,b,c 1607 0.15
a^2+b^2=c^2 Pythagoras a,b,c 1800 BC 0.20

I can’t say that the data is accurate, but it is data!

Note: I’m doing this in Python, so I don’t know where to find the dashAgGridComponentFunctions.js file after the pip install.

Hello @gdillon,

There is a way to do this without needing to use the Markdown renderer:

dashGridFunctions.js

var dagcompfuncs = window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {};

window.MathJax = {
  tex: {
    inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]
  },
};

(function () {
  var script = document.createElement('script');
  script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js';
  script.async = true;
  document.head.appendChild(script);
})();


dagcompfuncs.MathJax = (params) => {
    const ref = React.createRef()
    el = React.createElement('div', {ref}, params.value)
    React.useEffect(() => {
        if (window.MathJax) {
            window.MathJax.typeset([ref.current]);
        }
      }, [params.value]);
    return el
}

app.py:

import dash
from dash import *
import dash_ag_grid as dag
import pandas as pd

app = Dash(__name__,
           external_scripts=["https://polyfill.io/v3/polyfill.min.js?features=es6"])

data = [
    {'formula': '$E=mc^2$', 'inventor': 'Einstein', 'symbols': '$E,m,c$', 'year': 1905},
    {'formula': '$y=mx+b$', 'inventor': 'Salmon', 'symbols': '$y,m,x,b$', 'year': None},
    {'formula': '$x=\\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$', 'inventor': 'Brahmagupta', 'symbols': '$x,a,b,c$', 'year': 1607},
    {'formula': '$a^2+b^2=c^2$', 'inventor': 'Pythagoras', 'symbols': '$a,b,c$', 'year': '1800 BC'}
]

df = pd.DataFrame(data)

app.layout = html.Div(
    [
        dag.AgGrid(
            id='grid',
            rowData=df.to_dict('records'),
            columnDefs=[{'field': x, 'cellDataType': 'text',
                         'cellRenderer': 'MathJax' if x in ['formula', 'symbols'] else ''} for x in df.columns]
        )
    ]
)

app.run(debug=True)

Reference:
https://docs.mathjax.org/en/v3.2-latest/web/configuration.html#web-configuration

Thanks so much for the reply! Do you happen to know how I can modify the dashGridFunctions.js file? I can’t seem to find it in my directory.

Also, will this still work for column headers as well? Please refer to my updated sample data in the comment you originally replied to.

You need to make the file in the directory assets/ and then it will be autoloaded by Dash. :slight_smile:

To have it in the column, you need to have it as a custom component.

Here is the adjusted code:

var dagcompfuncs = window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {};

window.MathJax = {
  tex: {
    inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]
  },
};

(function () {
  var script = document.createElement('script');
  script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js';
  script.async = true;
  document.head.appendChild(script);
})();


dagcompfuncs.MathJax = (params) => {
    const value = (params.value || params.displayName)
    const ref = React.createRef()
    el = React.createElement('div', {ref}, value)
    React.useEffect(() => {
        if (window.MathJax) {
            window.MathJax.typeset([ref.current]);
        }
      }, [value]);
    return el
}

And here is the adjusted app for the column name:

import dash
from dash import *
import dash_ag_grid as dag
import pandas as pd

app = Dash(__name__,
           external_scripts=["https://polyfill.io/v3/polyfill.min.js?features=es6"])

data = [
    {'formula': '$E=mc^2$', 'inventor': 'Einstein', 'symbols': '$E,m,c$', 'year': 1905, '$\\alpha\\\$': 0.05},
    {'formula': '$y=mx+b$', 'inventor': 'Salmon', 'symbols': '$y,m,x,b$', 'year': None, '$\\alpha\\\$': 0.10},
    {'formula': '$x=\\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$', 'inventor': 'Brahmagupta', 'symbols': '$x,a,b,c$', 'year': 1607, '$\\alpha\\\$': 0.15},
    {'formula': '$a^2+b^2=c^2$', 'inventor': 'Pythagoras', 'symbols': '$a,b,c$', 'year': '1800 BC', '$\\alpha\\\$': 0.20}
]

df = pd.DataFrame(data)

app.layout = html.Div(
    [
        dag.AgGrid(
            id='grid',
            rowData=df.to_dict('records'),
            columnDefs=[{'field': x, 'cellDataType': 'text',
                         'cellRenderer': 'MathJax' if x in ['formula', 'symbols'] else '',
                         'headerComponent': 'MathJax' if x == '$\\alpha\\\$' else ''} for x in df.columns]
        )
    ]
)

app.run(debug=True)
1 Like

Here’s another solution using the dcc.Markdown component. It’s a little simpler, plus you can use the markdown component in other ways in the grid too

import dash_ag_grid as dag
from dash import Dash, html, dcc

data = [
    {"formula": "$E=mc^2$", "inventor": "Einstein", "symbols": "$E,m,c$", "year": 1905},
    {"formula": "$y=mx+b$", "inventor": "Salmon", "symbols": "$y,m,x,b$", "year": None},
    {"formula": "$x=\\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}$", "inventor": "Brahmagupta", "symbols": "$x,a,b,c$", "year": 1607},
    {"formula": "$a^2+b^2=c^2$", "inventor": "Pythagoras", "symbols": "$a,b,c$", "year": "1800 BC"}
]

columnDefs = [
    {
        "field": "formula",
        "cellRenderer": "DccMarkdown",
        "cellRendererParams": {"mathjax": True, "className":"markdown"},

    },
    {"field": "inventor"},
    {

        "field": "symbols",
        "cellRenderer": "DccMarkdown",
        "cellRendererParams": {"mathjax": True,  "className":"markdown"},

    },
    {"field": "year",  'cellDataType': 'text'}
]


grid = dag.AgGrid(
    columnDefs=columnDefs,
    rowData=data,
    columnSize="sizeToFit",

)


app = Dash(__name__)

app.layout = html.Div(
    [dcc.Markdown("Example of formating data with MathJax"), grid],
    style={"margin": 20},
)

if __name__ == "__main__":
    app.run(debug=True)




"""
Put the following in the dashAgGridComponentFunctions.js file in /assets

var dagcomponentfuncs = window.dashAgGridComponentFunctions = window.dashAgGridComponentFunctions || {};


dagcomponentfuncs.DccMarkdown = function (props) {
    return React.createElement(
        window.dash_core_components.Markdown, {
            children: props.value,
            className: props.className,
            style: props.style,
            dangerously_allow_html : props.dangerously_allow_html | false,
            link_target: props.link_target,
            mathjax: props.mathjax
        })
};

"""

"""
Put the following in a .css file in /assets

Note: The dcc.Markdown component wraps the content in a p element which can mess with the alignment when it's used
in the grid.  The following css will remove the extra margin.


.markdown p {
   margin: 0
}



"""


This worked great! However, I can no longer sort or filter my columns. I have deducted it down to this parameter:

columnDefs=[{
    ...
    "headerComponent": "MathJax",
    ...
} for x in df.columns]

When I remove that parameter, the rows sort and filter just fine. When I include that parameter, the columns are rendered with MathJax, but I lose the ability to filter or sort them. Is there a fix for this issue?

When you provide your own header component, you take responsibility for all of those handlers.

Checkout here:

Is there an easy way to keep all the default handlers? Or do I have to redefine everything? I’m having a difficult time understanding the documentation you sent. I don’t know React, JS, etc.

Here, try this:

js

var dagfuncs = window.dashAgGridFunctions = window.dashAgGridFunctions || {};

window.MathJax = {
  tex: {
    inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]
  },
};

(function () {
  var script = document.createElement('script');
  script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js';
  script.async = true;
  document.head.appendChild(script);
})();

dagfuncs.refreshMathJax = () => {
    window.MathJax.typeset();
}

app.py:

import dash
from dash import *
import dash_ag_grid as dag
import pandas as pd

app = Dash(__name__,
           external_scripts=["https://polyfill.io/v3/polyfill.min.js?features=es6"])

data = [
    {'formula': '$E=mc^2$', 'inventor': 'Einstein', 'symbols': '$E,m,c$', 'year': 1905, '$\\alpha\\\$': 0.05},
    {'formula': '$y=mx+b$', 'inventor': 'Salmon', 'symbols': '$y,m,x,b$', 'year': None, '$\\alpha\\\$': 0.10},
    {'formula': '$x=\\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$', 'inventor': 'Brahmagupta', 'symbols': '$x,a,b,c$', 'year': 1607, '$\\alpha\\\$': 0.15},
    {'formula': '$a^2+b^2=c^2$', 'inventor': 'Pythagoras', 'symbols': '$a,b,c$', 'year': '1800 BC', '$\\alpha\\\$': 0.20}
]

df = pd.DataFrame(data)

app.layout = html.Div(
    [
        dag.AgGrid(
            id='grid',
            rowData=df.to_dict('records'),
            columnDefs=[{'field': x, 'cellDataType': 'text'} for x in df.columns],
            eventListeners={'stateUpdated': ['refreshMathJax()']}
        )
    ]
)

app.run(debug=True)

This uses the new event listeners to add an event that listens for the state to be updated, and if the state is updated, then it will automatically apply the typeset again.

Using it this way means that you dont need the extra component for the cell renderer, however, there may be some performance impact from doing it this way.

1 Like

This was perfect! Thank you!

1 Like

Hey Grady … this sounds interesting. Can you upload a screenshot (or better yet an animated gif ) of the end result … with some well known formulas … I’d be keen to share such via a LinkedIn post to show a bit of art-of-the-possible going on here.