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

> Integrate Automattic harper with other systems using harper-core, harper-wasm, harper.js, or harper-ls. This guide provides component details and API examples for seamless integration.

- Repository: [Automattic/harper](https://github.com/Automattic/harper)
- Tags: how-to-guide
- Published: 2026-07-27

---

**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`](https://github.com/Automattic/harper/blob/main/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.

```javascript
// 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/main/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`.

```html
<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/main/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.

```bash

# 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`](https://github.com/Automattic/harper/blob/main/init.lua):

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

```

*Source:* [`packages/web/src/routes/docs/integrations/language-server/+page.md`](https://github.com/Automattic/harper/blob/master/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.

```rust
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.

```typescript
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/main/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`](https://github.com/Automattic/harper/blob/main/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.