How to Integrate Automattic/harper with Other Systems: Component Guide and API Examples

Integrate Automattic/harper with other systems by selecting the appropriate component—harper-core for native Rust, harper-wasm for browser JavaScript, harper.js for Node.js, or harper-ls for LSP-compatible editors—and invoking the unified linting API that processes text through Document construction, LintGroup rules, and Lint result emission.

Automattic/harper is a privacy-first grammar and spell-checking engine designed for modular integration across diverse environments. Whether you are building a browser extension, a VS Code plugin, or a custom Rust CLI tool, understanding how to integrate Automattic/harper with other systems allows you to embed on-device linting without network dependencies. The architecture separates the core engine from language bindings, enabling consistent behavior from WebAssembly to Language Server Protocol implementations.

Understanding Harper's Modular Component Architecture

Harper consists of six primary components that share a unified configuration model and dictionary system. Each component targets a specific integration environment while delegating actual linting to harper-core.

  • harper-core: The Rust-based grammar-checking engine located in the crate's src/linting directory. All higher-level tools use this for tokenization and rule application.
  • harper-wasm: Compiles harper-core to WebAssembly, exposing a minimal API in harper-wasm/src/lib.rs for JavaScript environments.
  • harper.js: A thin wrapper around the WASM binary distributed as the harper NPM package, providing idiomatic JavaScript/TypeScript bindings.
  • harper-ls: A Language Server Protocol implementation that wraps harper-core, providing JSON-RPC endpoints for editor integration.
  • harper-cli: A command-line interface for batch processing and CI pipelines.
  • Platform plugins: Reference implementations for VS Code, Obsidian, Chrome/Firefox, and WordPress that demonstrate embedding patterns.

Integration Methods and Implementation Examples

JavaScript and Node.js Applications

For Node.js, Deno, or bundler-based projects, install the harper package and initialize the WASM module. The createHarper function loads the binary and exposes lintText for synchronous analysis.

// Install: npm install harper
import { createHarper } from 'harper';

// Initialize the WASM module (loads .wasm binary on first call)
const harper = await createHarper();

// Lint text synchronously
const result = harper.lintText(`
  This is a sample text with a teh typo.
`);

// result.lints contains { message, span, suggestion, ... }
console.log(result.lints);

Source: [packages/harper.js/README.md](https://github.com/Automattic/harper/blob/master/packages/harper.js/README.md)

Browser-Based Applications (Raw WASM)

For browser environments where you want direct WASM control without the NPM wrapper, instantiate the module directly from harper_wasm_bg.wasm.

<script type="module">
  import init from './harper_wasm_bg.wasm';

  const harper = await init();
  
  const { lints } = harper.lint_text(`
    The quick brown fox jumps over the lazy dogg.
  `);
  console.log(lints);
</script>

Source: [harper-wasm/src/lib.rs](https://github.com/Automattic/harper/blob/master/harper-wasm/src/lib.rs)

Editor Integration via Language Server Protocol

For IDE support in VS Code, Neovim, Helix, Emacs, or Zed, run harper-ls as a background process. The server wraps harper-core and communicates via LSP requests and notifications.


# Install via Cargo

cargo install harper-ls --locked

# Start the server (editors launch this automatically)

harper-ls

Configure your editor's LSP client to point to the binary. For Neovim, add to init.lua:

require('lspconfig').harper_ls.setup{}

Source: packages/web/src/routes/docs/integrations/language-server/+page.md

Native Rust Development

Add harper-core as a dependency to integrate directly with the Rust API. The core flow involves Document construction, LintGroup rule curation, and Lint result processing.

use harper_core::linting::{Rule, RuleContext};

pub struct MyRule;

impl Rule for MyRule {
    fn name(&self) -> &'static str { "MyRule" }
    fn run(&self, ctx: &mut RuleContext) {
        for token in ctx.tokens() {
            if token.text == "foobar" {
                ctx.add_lint(token.span, "Avoid the word 'foobar'");
            }
        }
    }
}

// Register and use
let mut linter = harper_core::Linter::new();
linter.add_rule(Box::new(MyRule));

The entry points reside in harper-core/src/linting/, where LintGroup::new_curated initializes the default rule set.

VS Code Extension Development

For programmatic VS Code integration, use the Harper client library to register code action providers.

import * as vscode from 'vscode';
import { HarperClient } from 'harper-vscode';

export function activate(context: vscode.ExtensionContext) {
  const client = new HarperClient();
  context.subscriptions.push(
    vscode.languages.registerCodeActionsProvider(
      { language: 'javascript' },
      client
    )
  );
}

Source: [packages/vscode-plugin/README.md](https://github.com/Automattic/harper/blob/master/packages/vscode-plugin/README.md)

Core Linting Architecture Flow

Regardless of the integration point, all components follow the same five-stage pipeline implemented in harper-core:

  1. Input processing: Text enters via Document::new_* constructors, which build token streams and attach dictionaries (user, workspace, or static).
  2. Rule curation: LintGroup::new_curated instantiates the active rule set.
  3. Analysis: Each rule processes the token stream, identifying spans that violate grammar or spelling constraints.
  4. Result emission: Errors return as Lint objects containing message text, source spans, and suggested fixes.
  5. Consumer handling: The integration layer renders diagnostics, applies fixes via code actions, or forwards data to external services.

This architecture ensures that updates to harper-core automatically propagate to JavaScript, LSP, and CLI consumers.

Configuration and Privacy Model

All integrations respect Harper's privacy-first design: analysis occurs entirely on-device with no network calls. Configuration propagates through:

  • .harperrc files: Workspace-specific settings
  • JSON configuration: Used by harper-ls initialization
  • Programmatic API: Direct parameter passing in Rust or JavaScript

Dictionaries remain local, with user dictionaries stored in the workspace or home directory depending on the integration target.

Summary

  • Select by environment: Use harper.js for Node.js, harper-wasm for browsers, harper-ls for editors, and harper-core for native Rust.
  • Unified engine: All components delegate to harper-core in src/linting/, ensuring consistent rule application.
  • Standard flow: Construct a Document, apply LintGroup rules, and consume Lint results regardless of language binding.
  • Privacy guaranteed: No network calls; dictionaries and configuration remain local.
  • Reference implementations: Study packages/vscode-plugin, packages/obsidian-plugin, and packages/chrome-plugin for platform-specific patterns.

Frequently Asked Questions

Can I use Harper in a React or Vue frontend application?

Yes. Install the harper NPM package and call createHarper() to load the WASM module. The lintText method works in any modern browser environment that supports WebAssembly, including React, Vue, and vanilla JavaScript applications. The WASM binary is loaded asynchronously on first use.

How do I add custom grammar rules when integrating Harper into my Rust application?

Import harper-core and implement the Rule trait from src/linting/. Define the name() and run() methods to inspect tokens via RuleContext, then use ctx.add_lint() to flag issues. Register your rule with Linter::add_rule() before calling linter.lint(text).

Does the LSP server support multiple languages or just English?

The harper-ls binary supports any text document that harper-core can tokenize, including Markdown, code comments, and plain text. While the default dictionary is English, the architecture supports custom dictionaries specified in .harperrc or LSP initialization options.

Is it possible to run Harper in a CI pipeline for automated grammar checking?

Yes. Install harper-cli via Cargo and invoke it in your CI workflow. The CLI processes files recursively and exits with non-zero status if lints are found, making it suitable for pre-commit hooks and GitHub Actions workflows.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →