pn.pane.LaTeX displays raw latex in interactive displays

I just ran into the LaTeX rendering issue again, so I decided to post this solution for people searching for this…

Issue: pn.pane.LaTeX and pn.pane.Markdown often fail to render LaTeX in Jupyter notebooks, showing raw code instead.

Cause: MathJax rendering is not automatically triggered in Panel outputs within Jupyter.

Solution: Use pn.pane.LaTeX(..., renderer='katex'), which supports inline rendering in notebooks, and format math using supported block environments like aligned for multiline output.

Example Code:

class VectorAnglePlot:
    def __init__(self):
        self.angle_deg = 45
        self.plot_obj, self.text_obj = self._make_plot_and_text(self.angle_deg)

    def _make_plot_and_text(self, angle_deg):
        angle_rad = np.deg2rad(angle_deg)
        u = np.array([1.0, 0.0])
        v = np.array([np.cos(angle_rad), np.sin(angle_rad)])
        dot_product = np.dot(u, v)

        # --- Plot ---
        segments = hv.Segments([
            (0, 0, u[0], u[1]),
            (0, 0, v[0], v[1])
        ]).opts(color='blue', line_width=3)

        annotation = hv.Text(0.5, 1.6, f"Angle: {angle_deg:.1f}°").opts(color='black')

        plot = (segments * annotation).opts(
            width=400, height=400, xlim=(-1.5, 1.5), ylim=(0, 1.5),
            aspect='equal', title="Angle Between Two Vectors"
        )

        # --- Custom LaTeX string ---
        latex = self._latex_text(v[0], v[1], dot_product, angle_deg)
        text = pn.pane.LaTeX(latex, renderer='katex', width=300)
        return pn.pane.HoloViews(plot), text

    def _latex_text(self, vx, vy, dot, angle):
        return (
            r"$\Large \begin{aligned}"
            r"\vec{u} = \begin{bmatrix} 1.00 \\ 0.00 \end{bmatrix}  \\ "
            rf"\vec{{v}} = \begin{{bmatrix}} {vx:.2f} \\ {vy:.2f} \end{{bmatrix}} \\ "
            rf"\vec{{u}} \cdot \vec{{v}} = {dot:.2f} \\ "
            r"\\"
            rf"\theta = {angle:.2f}^\circ"
            "\end{aligned}$"
        )

    def update(self, angle_deg):
        self.angle_deg = angle_deg
        plot, text = self._make_plot_and_text(angle_deg)
        self.plot_obj.object = plot.object
        self.text_obj.object = text.object

    def layout(self):
        slider = pn.widgets.IntSlider(name="Angle (degrees)", start=0, end=180, value=self.angle_deg)
        slider.param.watch(lambda e: self.update(e.new), 'value')
        return pn.Column(slider, pn.Row(self.plot_obj, pn.Spacer(width=20),
                                 pn.Column(pn.Spacer(height=20),self.text_obj)))
VectorAnglePlot().layout()
1 Like