Use highlight.js to implement code syntax highlighting in ToolMi

It introduces in detail how to integrate highlight.js into the Vue 3 project to achieve code syntax highlighting, including complete technical selection, implementation process and pit experience sharing.

Project Background and Requirements Analysis

As the developer of ToolMi(gongjumi.com), I’m always striving to provide users with a better online tools experience. Recently, while upgrading the site’s code display feature, I discovered that the native textarea component has significant UX limitations when handling code input and output.

Gongjumi offers many code-related tools, such as the YAML Formatter, the SQL to Java Entity Generator, and the TypeScript Beautifier. All of these require users to input code and display processed results. However, plain textareas lack syntax highlighting, making it hard for users to locate errors in complex code and diminishing the overall experience.

Technology Selection: Why highlight.js

Among the many syntax-highlighting libraries available, I chose highlight.js for several key reasons:

Comprehensive Language Support

highlight.js supports over 190 programming languages—from popular languages like JavaScript, Python, and Java to more esoteric functional languages. Such broad coverage is essential for a site like Gongjumi that handles many code formats.

Rich Theme Library

The library offers a variety of theme styles, including GitHub, VS Code, and Atom themes. This allows me to pick a code display theme that aligns with Gongjumi’s overall design aesthetic.

Great Ecosystem Compatibility

highlight.js integrates smoothly with the Vue.js ecosystem and is relatively straightforward to set up. It also features automatic language detection, so it can intelligently apply highlighting even if the user doesn’t specify the language.

Performance Advantages

Compared to heavier code editors like Monaco Editor, highlight.js is much lighter and loads faster—ideal for Gongjumi’s need for quick, responsive online tools.

Implementation Details

First Attempt: Two-Layer Overlay

Initially, I tried the common two-layer approach: overlaying a div for highlighted code over a transparent textarea.

<div class="editor-container">
  <div class="code-highlight" v-html="highlightedCode"></div>
  <textarea v-model="code" class="code-textarea"></textarea>
</div>

This setup lets users type in the transparent textarea while the background layer shows the highlighted code. In practice, though, it had critical flaws:

  • The highlight layer and the input layer often misaligned, especially with long code and many line breaks.
  • Keeping scroll positions in sync was difficult; scrolling one layer didn’t always match the other.
  • Matching fonts, line-heights, and padding exactly proved very tricky.

CSS Grid Layout Optimization

To fix alignment, I tried placing both elements in the same grid area using CSS Grid:

.editor-container {
  display: grid;
  grid-template-areas: "editor";
}
.code-highlight, .code-textarea {
  grid-area: editor;
}

This improved alignment, but scroll sync and style consistency remained challenging.

Final Solution: contenteditable Div

After many experiments, I found the best approach is to use a contenteditable div:

<div
  contenteditable="true"
  class="editable-code hljs"
  v-html="highlightedCode"
  @input="handleInput"
/>

This approach offers clear advantages:

  • It uses highlight.js’s native CSS styles without extra adaptation.
  • No complex overlay structure—so alignment and sync issues disappear.
  • Smoother UX, with editing and display fully unified.

Core Code Implementation

Component Interface Design

To make the code editor reusable across Gongjumi’s tools, I designed a clear interface:

interface Props {
  modelValue: string      // two-way bound code content
  language?: string       // optional language specifier
  readonly?: boolean      // read-only mode
  placeholder?: string    // placeholder text
  rows?: number           // number of visible lines
}

Syntax Highlighting Logic

The heart of highlighting is calling the highlight.js API correctly and handling errors gracefully:

const updateHighlight = () => {
  try {
    if (props.language && hljs.getLanguage(props.language)) {
      // use the specified language
      const result = hljs.highlight(localValue.value, { 
        language: props.language 
      });
      highlightedCode.value = result.value;
    } else {
      // auto-detect language
      const result = hljs.highlightAuto(localValue.value);
      highlightedCode.value = result.value;
    }
  } catch (error) {
    console.error('Syntax highlighting error:', error);
    // fallback: show raw escaped code
    highlightedCode.value = escapeHtml(localValue.value);
  }
};

Edit Mode vs. Read-Only Mode

Since Gongjumi needs both input and display scenarios, I implemented two modes:

<template>
  <!-- Read-only mode for displaying processed results -->
  <pre v-if="readonly" class="readonly-code hljs" v-html="highlightedCode"></pre>

  <!-- Edit mode for user input -->
  <div v-else contenteditable="true" class="editable-code hljs" @input="handleInput"></div>
</template>

Key Technical Details

Theme Integration

To align with Gongjumi’s design, I chose the GitHub-style theme and tweaked some colors:

// import highlight.js theme
import 'highlight.js/styles/github.css';

User Interaction Enhancements

In practice, I needed to handle several special interactions:

  • Tab key handling: insert proper spaces instead of moving focus.
  • Paste handling: strip formatting, keep only plain text.
  • Input debounce: prevent excessive highlight updates during fast typing.

Preserving Cursor Position

Because contenteditable and innerHTML updates can reset the cursor, I implemented save-and-restore logic:

const handleInput = () => {
  // save current cursor position
  const cursorPosition = saveCursorPosition();

  // update content and highlighting
  updateContent();
  updateHighlight();

  // restore cursor position after DOM update
  nextTick(() => {
    restoreCursorPosition(cursorPosition);
  });
};

Real-World Application Effects

SQL to Java Entity Tool Upgrade

One of Gongjumi’s popular tools is the SQL-to-Java-entity converter. Before, users typed SQL into a plain textarea and got plain-text output. After integrating highlight.js:

  • The input box supports full SQL syntax highlighting, making keywords, field names, and types easy to identify.
  • The generated Java output now features professional syntax highlighting.
  • The overall UI looks more polished, and user feedback has been very positive.

JSON Formatter Enhancement

JSON formatting is one of the most-used tools on Gongjumi. With syntax highlighting, users can clearly see structure levels and spot errors faster.

TypeScript Beautifier

The refactor impact on the TypeScript beautifier was the greatest. What used to be simple <pre> and <code> tags is now a full code-editor component, offering an IDE-like experience.

Performance Optimizations and Best Practices

On-Demand Language Loading

Although highlight.js supports 190+ languages, Gongjumi typically uses only a handful. To reduce bundle size, I load only the common ones:

// import core and only needed languages
import hljs from 'highlight.js/lib/core';
import javascript from 'highlight.js/lib/languages/javascript';
import python from 'highlight.js/lib/languages/python';
import java from 'highlight.js/lib/languages/java';
import sql from 'highlight.js/lib/languages/sql';

hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('python', python);
hljs.registerLanguage('java', java);
hljs.registerLanguage('sql', sql);

Component Lifecycle Management

Proper lifecycle handling is crucial for performance and memory:

  • Initialize highlight.js on mount.
  • Update highlighting on component updates.
  • Clean up resources on unmount.

Responsive Design Considerations

Many Gongjumi users are on mobile, so the editor needs to adapt:

  • Appropriate font sizes and line heights.
  • Touch-friendly interactions.
  • Sensible scrolling and zoom behaviors.

Lessons Learned

Common Issues and Solutions

Layer Misalignment

This was the first major issue. The solution was to abandon the two-layer approach and use a contenteditable div. If you must use two layers, prefer CSS Grid over absolute positioning.

Cursor Jumping

Frequent DOM updates can disturb the cursor. Solved by saving/restoring cursor position and debouncing updates.

Style Conflicts

highlight.js styles can clash with existing site styles. Use CSS scoping or CSS Modules to isolate styles and ensure highlight styles remain independent.

Debugging Tips

  • Use the browser dev tools’ Elements panel to inspect DOM changes.
  • Monitor highlight.js errors and warnings in the console.
  • Test with different languages and code lengths.
  • Verify across browsers and devices.

Future Expansion Plans

Feature Enhancements

Based on user feedback and usage data, I plan to add more useful features to Gongjumi’s code editor:

  • Line Numbers: Help users locate lines in longer snippets.
  • Code Folding: Collapse/expand functions, classes, etc.
  • Search & Replace: Quickly find and replace text within code.
  • Integrated Formatting: One-click formatting via Prettier or similar.

Further Performance Tuning

  • Implement virtual scrolling to handle very large files.
  • Optimize highlighting algorithms to reduce latency on big files.
  • Add caching to avoid redundant syntax analysis.

Project Summary and Reflection

Technical Gains

This integration of highlight.js taught me the internals of modern front-end code editors, advanced user-interaction handling, and rich component-driven development practices.

User Value Delivered

Most importantly, this upgrade delivered real value: user feedback shows the new syntax highlighting dramatically improves the code tools’ UX, especially in error detection and comprehension of complex code.

Architectural Insights

This project underscored the importance of component-based design. A well-designed code editor component not only serves current needs but also lays a solid foundation for future features and reuse across projects.

For an online tools site like Gongjumi, every UX detail matters. Code syntax highlighting may seem minor, but it reflects deep user understanding and relentless pursuit of product quality.