npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@markitjs/angular

v1.0.4

Published

Angular bindings for @markitjs/core text highlighting engine

Readme

@markitjs/angular

Angular bindings for the @markitjs/core text highlighting engine. Provides a standalone MarkitHighlightDirective and an injectable MarkitService.

Install

npm install @markitjs/angular
# or
bun add @markitjs/angular
# or
pnpm add @markitjs/angular
# or (Yarn — include @markitjs/core explicitly)
yarn add @markitjs/angular @markitjs/core

Peer dependencies: Angular 17, 18, or 19, and @markitjs/core (installed automatically by npm 7+, pnpm, and bun; with Yarn, add @markitjs/core explicitly: yarn add @markitjs/angular @markitjs/core).

Quick Start

Directive

Import the standalone directive in your component:

import { Component } from '@angular/core';
import { MarkitHighlightDirective } from '@markitjs/angular';

@Component({
  selector: 'app-search',
  standalone: true,
  imports: [MarkitHighlightDirective],
  template: `
    <input [(ngModel)]="searchTerm" placeholder="Search..." />

    <div [markitHighlight]="searchTerm" [markitOptions]="highlightOptions">
      <p>This content will be searched and highlighted.</p>
      <app-child></app-child>
      <!-- bindings preserved -->
    </div>
  `,
})
export class SearchComponent {
  searchTerm = '';
  highlightOptions = { caseSensitive: false, accuracy: 'partially' as const };
}

Service

For programmatic control, inject MarkitService:

import { Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';
import { MarkitService } from '@markitjs/angular';
import type { MarkitInstance } from '@markitjs/core';

@Component({
  selector: 'app-editor',
  template: `<div #content>Searchable content here.</div>`,
})
export class EditorComponent implements OnDestroy {
  @ViewChild('content', { static: true }) contentRef!: ElementRef<HTMLElement>;

  private instance: MarkitInstance | null = null;

  constructor(private markitService: MarkitService) {}

  search(term: string) {
    this.instance?.destroy();
    this.instance = this.markitService.create(this.contentRef.nativeElement);
    this.markitService.highlight(this.instance, term, {
      renderer: 'dom',
      caseSensitive: false,
    });
  }

  clear() {
    this.markitService.clear(this.instance!);
  }

  ngOnDestroy() {
    this.instance?.destroy();
  }
}

API

MarkitHighlightDirective

Standalone attribute directive. Apply to any element whose text content you want to highlight.

| Input | Type | Description | | ------------------ | ------------------------------------------ | -------------------------------------------------------------------------------------------------------- | | markitHighlight | string \| string[] | Search term(s) to highlight | | markitOptions | Partial<MarkitOptions> | All @markitjs/core options (renderer, accuracy, etc.) | | markitPlugins | MarkitPlugin[] | Plugins to register | | markitContentKey | string \| number \| (string \| number)[] | When content is dynamic, pass value(s) that change with content so the directive unmarks and re-applies. |

For dynamic content (e.g. bound to a signal), pass [markitContentKey] so highlights re-apply after content updates and avoid garbled text. See Framework lifecycles for how the Angular highlight cycle works.

MarkitService

Injectable service (providedIn: 'root'). All operations run outside NgZone.

| Method | Description | | --------------------------------------------- | -------------------------- | | create(element, plugins?) | Create a MarkitInstance | | highlight(instance, term, options?) | Apply keyword highlighting | | highlightRegExp(instance, regexp, options?) | Apply regex highlighting | | clear(instance) | Remove all highlights |

NgZone Safety

Both the directive and service run highlighting outside NgZone to avoid triggering unnecessary change detection cycles. This is especially important for DOM-mutating renderers.

Callbacks (done, noMatch) automatically re-enter the zone, so updating component state from callbacks works correctly:

highlightOptions = {
  done: (count: number) => {
    this.matchCount = count; // safe — re-enters NgZone automatically
  },
};

Change Detection Compatibility

| Strategy | Works? | Notes | | ------------ | ------ | ---------------------------------------------------------- | | Default | Yes | No unnecessary cycles triggered | | OnPush | Yes | Changes fire via @Input bindings through OnChanges | | Signals | Yes | Bind a signal in the template; directive reacts via inputs | | Zoneless | Yes | Operations already run outside zone |

Preserving Bindings

Unlike innerHTML-based approaches, MarkIt never replaces DOM nodes. The CSS Highlight API (default) creates zero DOM mutations. The DOM wrapping renderer splits text nodes and wraps matches while keeping the original text node in place—including when the match is at the start of the text—so framework bindings (e.g. {{ value }}) continue to update correctly and it never destroys component instances, event listeners, or form control state.

Batched Rendering

For large content areas, enable batched rendering:

<div [markitHighlight]="searchTerm" [markitOptions]="{ batchSize: 500, done: onHighlightDone }">
  <!-- large content -->
</div>

Performance tip: For large documents or fast typing (e.g. live search), pass debounce and/or batchSize in markitOptions. debounce (ms) reduces re-index/render cycles when the term changes quickly; batchSize splits rendering across animation frames so the UI stays responsive. Both are supported by the core engine and work with the directive and service.

CSS Highlight API Styling

When using the default auto renderer on supported browsers, add this to your global styles:

::highlight(markit-highlight) {
  background-color: #fef08a;
  color: inherit;
}

License

MIT