Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

latex2vue

Vue.js components for LaTeX rendering with full support for mathematical notation, PSTricks graphics, and interactive elements.

Installation

npm install latex2vue

Features

  • Vue Integration: Native Vue components with reactive data binding
  • LaTeX Rendering: Complete LaTeX document and expression support
  • Interactive Graphics: PSTricks support with sliders and animations
  • Mathematical Notation: Seamless MathJax integration

Basic Usage

Vue 3 with Composition API

<template>
  <div>
    <h1>Mathematical Document</h1>
    <latex :content="content" />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { latex } from 'latex2vue';

const content = ref(`
  \\section{Introduction}
  Welcome to LaTeX2JS! Here's the quadratic formula:
  $$x = \\frac{-b \\pm \\sqrt{b^2-4ac}}{2a}$$
  
  \\begin{pspicture}(-2,-2)(2,2)
    \\psgrid
    \\psplot[linecolor=red]{-2}{2}{x dup mul}
    \\rput(0,1.5){$y = x^2$}
  \\end{pspicture}
`);
</script>

Vue 3 with Options API

<template>
  <div>
    <h1>Mathematical Document</h1>
    <latex :content="content" />
  </div>
</template>

<script>
import { latex } from 'latex2vue';

export default {
  components: {
    latex
  },
  data() {
    return {
      content: `
        \\section{Introduction}
        Welcome to LaTeX2JS! Here's Euler's identity:
        $$e^{i\\pi} + 1 = 0$$
      `
    };
  }
};
</script>

Installation and Setup

Vue 3 Plugin Installation

import { createApp } from 'vue';
import LaTeX2Vue from 'latex2vue';
import App from './App.vue';

const app = createApp(App);
app.use(LaTeX2Vue);
app.mount('#app');

Nuxt.js Plugin

Create plugins/latex2vue.js:

import LaTeX2Vue from 'latex2vue';

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(LaTeX2Vue);
});

Add to nuxt.config.js:

export default {
  plugins: [
    { src: '~/plugins/latex2vue.js', mode: 'client' }
  ]
};

API Reference

latex Component

The main component for rendering LaTeX content:

<template>
  <latex
    :content="latexString"
    :macros="macroDefinitions"
    :config="mathJaxConfig"
    class="custom-class"
    @render="onRender"
    @error="onError"
  />
</template>

Props

  • content (String, required): LaTeX content to render
  • macros (String, optional): Custom macro definitions
  • config (Object, optional): MathJax configuration
  • class (String, optional): CSS class name
  • style (Object, optional): Vue style object

Events

  • @render: Emitted after successful rendering with rendered element
  • @error: Emitted if rendering fails with error object

Individual Components

For specific LaTeX environments:

<template>
  <div>
    <!-- PSTricks graphics -->
    <pspicture :content="graphicsContent" />
    
    <!-- Styled information boxes -->
    <nicebox :title="boxTitle" :content="boxContent" />
    
    <!-- Mathematical expressions -->
    <math-display :content="mathExpression" />
    
    <!-- Lists and enumerations -->
    <latex-enumerate :content="listContent" />
    
    <!-- Code blocks -->
    <latex-verbatim :content="codeContent" />
  </div>
</template>

Usage Examples

Mathematical Documents

<template>
  <div class="math-document">
    <latex :content="document" />
  </div>
</template>

<script setup>
import { ref } from 'vue';

const document = ref(`
  \\title{Advanced Calculus}
  \\author{Dr. Smith}
  \\date{\\today}
  \\maketitle
  
  \\section{Limits and Continuity}
  A function $f$ is continuous at $x = a$ if:
  $$\\lim_{x \\to a} f(x) = f(a)$$
  
  \\begin{theorem}[Intermediate Value Theorem]
  If $f$ is continuous on $[a,b]$ and $f(a) < k < f(b)$, 
  then there exists $c \\in (a,b)$ such that $f(c) = k$.
  \\end{theorem}
`);
</script>

Interactive Graphics

<template>
  <div>
    <h2>Interactive Sine Wave</h2>
    <div class="controls">
      <label>
        Amplitude: {{ amplitude }}
        <input 
          v-model.number="amplitude"
          type="range" 
          min="0.1" 
          max="3" 
          step="0.1"
        />
      </label>
      <label>
        Frequency: {{ frequency }}
        <input 
          v-model.number="frequency"
          type="range" 
          min="0.1" 
          max="3" 
          step="0.1"
        />
      </label>
    </div>
    <latex :content="plotContent" />
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';

const amplitude = ref(1);
const frequency = ref(1);

const plotContent = computed(() => `
  \\begin{pspicture}(-6,-3)(6,3)
    \\userline{amplitude}{0.1}{3}{${amplitude.value}}
    \\userline{frequency}{0.1}{3}{${frequency.value}}
    \\psplot[linecolor=blue]{-6}{6}{amplitude frequency x mul sin mul}
    \\rput(-5,2.5){Amplitude: ${amplitude.value.toFixed(2)}}
    \\rput(-5,2){Frequency: ${frequency.value.toFixed(2)}}
  \\end{pspicture}
`);
</script>

Custom Macros

<template>
  <latex :content="content" :macros="customMacros" />
</template>

<script setup>
import { ref } from 'vue';
import baseMacros from '@latex2js/macros';

const customMacros = ref(`
  ${baseMacros}
  \\newcommand{\\R}{\\mathbb{R}}
  \\newcommand{\\norm}[1]{\\left\\|#1\\right\\|}
  \\newcommand{\\inner}[2]{\\langle #1, #2 \\rangle}
`);

const content = ref(`
  \\section{Vector Spaces}
  Let $V$ be a vector space over $\\R$. For any $\\vec{v}, \\vec{w} \\in V$:
  
  \\begin{enumerate}
    \\item The norm satisfies: $\\norm{\\vec{v}} \\geq 0$
    \\item Inner product: $\\inner{\\vec{v}}{\\vec{w}} = \\inner{\\vec{w}}{\\vec{v}}$
    \\item Cauchy-Schwarz: $|\\inner{\\vec{v}}{\\vec{w}}| \\leq \\norm{\\vec{v}} \\norm{\\vec{w}}$
  \\end{enumerate}
`);
</script>

Dynamic Content

<template>
  <div>
    <div class="input-section">
      <label>
        Enter function (LaTeX format): 
        <input 
          v-model="formula"
          type="text" 
          placeholder="x^2 + 2x + 1"
        />
      </label>
    </div>
    <latex :content="latexContent" />
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';

const formula = ref('x^2 + 2x + 1');

const latexContent = computed(() => `
  \\section{Function Analysis}
  Consider the function: $f(x) = ${formula.value}$
  
  \\subsection{Graph}
  \\begin{pspicture}(-3,-1)(3,5)
    \\psaxes{->}(0,0)(-3,-1)(3,5)
    \\psplot[linecolor=red]{-3}{3}{x dup mul 2 x mul add 1 add}
    \\rput(0,4.5){$f(x) = ${formula.value}$}
  \\end{pspicture}
`);
</script>

Error Handling

<template>
  <div>
    <latex 
      :content="content"
      @render="onRenderSuccess"
      @error="onRenderError"
    />
    <div v-if="error" class="error-message">
      Error: {{ error.message }}
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const content = ref(`
  \\section{Mathematical Expressions}
  This might contain invalid LaTeX: $\\invalid{command}$
`);

const error = ref(null);

const onRenderSuccess = (element) => {
  console.log('LaTeX rendered successfully:', element);
  error.value = null;
};

const onRenderError = (err) => {
  console.error('LaTeX rendering error:', err);
  error.value = err;
};
</script>

Integration Examples

With Vue Router

<template>
  <router-view />
</template>

<script setup>
import { provide } from 'vue';

// Provide global LaTeX configuration
provide('latexConfig', {
  mathJax: {
    tex: {
      inlineMath: [['$', '$']],
      displayMath: [['$$', '$$']],
      packages: ['base', 'ams', 'physics']
    }
  }
});
</script>

With Pinia/Vuex State Management

<template>
  <div class="math-editor">
    <textarea 
      v-model="editorContent"
      placeholder="Enter LaTeX content..."
    />
    <div class="preview">
      <latex :content="editorContent" :macros="editorMacros" />
    </div>
  </div>
</template>

<script setup>
import { storeToRefs } from 'pinia';
import { useEditorStore } from '@/stores/editor';

const editorStore = useEditorStore();
const { content: editorContent, macros: editorMacros } = storeToRefs(editorStore);
</script>

Nuxt.js Integration

<!-- pages/math.vue -->
<template>
  <div>
    <h1>{{ title }}</h1>
    <client-only>
      <latex :content="content" />
      <template #fallback>
        <div>Loading mathematical content...</div>
      </template>
    </client-only>
  </div>
</template>

<script setup>
definePageMeta({
  title: 'Mathematical Content'
});

const { data } = await $fetch('/api/math-content');

const title = ref(data.title);
const content = ref(data.latex);
</script>

Composition API Composable

// composables/useLatex.js
import { ref, computed } from 'vue';

export function useLatex(initialContent = '') {
  const content = ref(initialContent);
  const macros = ref('');
  const error = ref(null);
  const isRendering = ref(false);

  const latexWithMacros = computed(() => {
    if (macros.value) {
      return `${macros.value}\n${content.value}`;
    }
    return content.value;
  });

  const setContent = (newContent) => {
    content.value = newContent;
    error.value = null;
  };

  const addMacro = (macroDefinition) => {
    macros.value += `\n${macroDefinition}`;
  };

  const onRender = () => {
    isRendering.value = false;
    error.value = null;
  };

  const onError = (err) => {
    isRendering.value = false;
    error.value = err;
  };

  return {
    content,
    macros,
    error,
    isRendering,
    latexWithMacros,
    setContent,
    addMacro,
    onRender,
    onError
  };
}

Usage of the composable:

<template>
  <div>
    <textarea v-model="content" />
    <latex 
      :content="latexWithMacros"
      @render="onRender"
      @error="onError"
    />
    <div v-if="error">Error: {{ error.message }}</div>
  </div>
</template>

<script setup>
import { useLatex } from '@/composables/useLatex';

const {
  content,
  latexWithMacros,
  error,
  onRender,
  onError,
  addMacro
} = useLatex('Initial LaTeX content');

// Add custom macros
addMacro('\\newcommand{\\R}{\\mathbb{R}}');
</script>

TypeScript Support

// types/latex.ts
export interface LaTeXProps {
  content: string;
  macros?: string;
  config?: MathJaxConfig;
  class?: string;
  style?: StyleValue;
}

export interface MathJaxConfig {
  tex?: {
    inlineMath?: string[][];
    displayMath?: string[][];
    packages?: string[];
    macros?: Record<string, string>;
  };
}

Component with TypeScript:

<template>
  <latex 
    :content="content"
    :macros="macros"
    :config="config"
    @render="handleRender"
    @error="handleError"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import type { MathJaxConfig } from '@/types/latex';

interface Props {
  formula: string;
  title?: string;
  interactive?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  title: 'Mathematical Expression',
  interactive: false
});

const config: MathJaxConfig = {
  tex: {
    inlineMath: [['$', '$']],
    displayMath: [['$$', '$$']],
    packages: ['base', 'ams', 'physics']
  }
};

const content = ref(props.interactive 
  ? `\\userline{param}{0}{10}{5}\\psplot{-5}{5}{param x mul}`
  : `$$${props.formula}$$`);

const macros = ref('\\newcommand{\\R}{\\mathbb{R}}');

const handleRender = (element: HTMLElement) => {
  console.log('Rendered:', element);
};

const handleError = (error: Error) => {
  console.error('Error:', error);
};
</script>

Styling

<style scoped>
/* Component-specific styles */
.latex2js-content {
  font-family: 'Computer Modern', 'Latin Modern Math', serif;
  line-height: 1.6;
  margin: 1em 0;
}

.latex2js-pspicture {
  display: block;
  margin: 1em auto;
  max-width: 100%;
}

.latex2js-nicebox {
  border: 2px solid #42b883;
  border-radius: 8px;
  padding: 1em;
  margin: 1em 0;
  background: linear-gradient(145deg, #f0f9f4, #e8f5ea);
}

/* Responsive design */
@media (max-width: 768px) {
  .latex2js-pspicture {
    max-width: 100vw;
    transform: scale(0.8);
  }
}
</style>

<style>
/* Global styles */
.latex2js-math {
  overflow-x: auto;
  overflow-y: hidden;
}
</style>

Performance Tips

  1. Use v-memo: For static content that doesn't change
  2. Computed Properties: For reactive LaTeX content
  3. Lazy Loading: Use dynamic imports for large content
  4. Component Memoization: Cache rendered components
  5. Virtual Scrolling: For lists of mathematical expressions
<template>
  <!-- Use v-memo for static content -->
  <latex 
    v-memo="[staticContent]"
    :content="staticContent"
  />
  
  <!-- Use computed for reactive content -->
  <latex :content="computedContent" />
</template>

<script setup>
import { ref, computed } from 'vue';

const staticContent = ref('$$E = mc^2$$');
const parameter = ref(1);

const computedContent = computed(() => `
  \\begin{pspicture}(-2,-2)(2,2)
    \\psplot[linecolor=red]{-2}{2}{${parameter.value} x mul}
  \\end{pspicture}
`);
</script>

Browser Compatibility

  • Chrome/Chromium 60+
  • Firefox 60+
  • Safari 10.1+
  • Edge 79+
  • Vue 3.0+ (Composition API support)

Dependencies

  • vue: Peer dependency (>=3.0.0)
  • latex2js: Core parsing engine
  • mathjaxjs: MathJax integration
  • @latex2js/macros: Predefined macros
  • @latex2js/pstricks: Graphics support
  • @latex2js/utils: Core utilities

License

See LICENSE file in the repository root.