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

@adalink/spark-echo

v1.0.0

Published

Echo reactive web components library

Readme

⚡ Spark Echo Library

npm version License Build Status Code Style

Echo reactive web components library. Zero dependencies, reactive event-driven architecture.


📖 What is Echo?

Echo is a modern, lightweight library that provides a reactive event arc system for Web Components. Built on native Web Components APIs, it enables components to communicate through a declarative event routing system with powerful data transformation capabilities.

🎯 Why Choose Echo?

  • Zero Dependencies - No runtime dependencies, just pure Web Platform APIs
  • Declarative Event Arcs - Describe component communication via HTML attributes
  • Reactive by Default - Components automatically respond to events
  • Framework Agnostic - Works with any framework or vanilla JavaScript
  • Performance First - Optimized for speed with minimal bundle size (~2KB)
  • Production Ready - Battle-tested in real-world applications

🚀 Perfect For

  • Web Components Projects - Add reactive communication to native Web Components
  • Event-Driven Architecture - Build applications with pub/sub patterns
  • Performance-Critical Apps - Minimal overhead, maximum speed
  • Micro-Frontends - Isolated components with clean event interfaces
  • Real-time Updates - Reactive data flows between components

✨ Key Features

🎯 Declarative Event Arcs

Describe component communication declaratively using HTML attributes:

// Traditional approach
class MyComponent extends HTMLElement {
  connectedCallback() {
    document.addEventListener('custom-event', this.handleEvent.bind(this));
  }

  disconnectedCallback() {
    document.removeEventListener('custom-event', this.handleEvent.bind(this));
  }

  handleEvent(event) {
    if (event.detail.id === 'some-component') {
      this.value = event.detail.data;
    }
  }
}

// Echo approach
class MyComponent extends Echo(HTMLElement) {}

// In HTML:
<my-component on="*custom-event:attribute/value"></my-component>

📦 Simple API

Only import what you need:

import Echo, { filter } from '@adalink/spark-echo';

// Create reactive component
class MyComponent extends Echo(HTMLElement) {}

⚡ High Performance

  • Event delegation (single listener per component)
  • Automatic cleanup (no memory leaks)
  • Lazy event registration
  • No framework overhead

🚀 Quick Start

Installation

# Using npm
npm install @adalink/spark-echo

# Using yarn
yarn add @adalink/spark-echo

# Using pnpm
pnpm add @adalink/spark-echo

# Using bun
bun add @adalink/spark-echo

Your First Reactive Component

Create a reactive counter with automatic event propagation:

import Echo, { filter } from '@adalink/spark-echo';

class Counter extends Echo(HTMLElement) {
  #count = 0;

  static observedAttributes = ['count'];

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'count') {
      this.#count = parseInt(newValue, 10);
      this.updateDisplay();
    }
  }

  connectedCallback() {
    this.updateDisplay();
  }

  updateDisplay() {
    this.innerHTML = `
      <div class="counter">
        <h2>Count: ${this.#count}</h2>
        <button class="increment">+1</button>
        <button class="reset">Reset</button>
      </div>
    `;

    this.querySelector('.increment').addEventListener('click', () => {
      this.#count++;
      this.dispatchEvent(new CustomEvent('change', {
        detail: this.#count,
        bubbles: true,
        composed: true
      }));
    });

    this.querySelector('.reset').addEventListener('click', () => {
      this.#count = 0;
      this.dispatchEvent(new CustomEvent('change', {
        detail: this.#count,
        bubbles: true,
        composed: true
      }));
    });
  }
}

customElements.define('my-counter', Counter);

Use it in your HTML:

<my-counter id="counter1" count="0"></my-counter>

<!-- Another component that listens to counter changes -->
<my-display on="#counter1/change:method/handleUpdate"></my-display>

📦 Event Arc Syntax

The power of Echo comes from its declarative event arc syntax:

Arc Format

source/event:type/sink|filter1|filter2

Components

  • source: Where the event comes from

    • * - Any component
    • #id - Specific component by ID
    • name - Component by name attribute
    • node - Component by node name (tag)
  • event: Event type to listen for

    • click, change, input, custom-event, etc.
  • type: How to apply the result

    • method - Call a method on this component
    • attribute - Set an HTML attribute
    • setter - Set a property
  • sink: Where to apply the result

    • Method name, attribute name, or property name
  • filters: Data transformations (optional, multiple with |)

    • filtername=value - Apply filter with parameter

Examples

<!-- Listen to any component's click events and call handleClick -->
<my-component on="*click:method/handleClick"></my-component>

<!-- Listen to specific component's change events and set value attribute -->
<my-display on="#counter/change:attribute/value"></my-display>

<!-- Listen to component by name and set property -->
<my-log on="logger/input:setter/message"></my-log>

<!-- Multiple filters: add 10 then convert to string -->
<my-component on="*data:method/handleData|add=10|toString"></my-component>

🎯 Available Filters

Echo includes powerful data transformation filters:

Basic Filters

import filter from '@adalink/spark-echo/filter';
  • truthy - Filter truthy values
  • always - Always return the value
  • equals - Compare with value
  • different - Compare for difference

Math Filters

  • add - Add value: |add=10
  • subtract - Subtract value: |subtract=5
  • inc - Increment by 1: |inc
  • dec - Decrement by 1: |dec

Comparison Filters

  • gt - Greater than: |gt=10
  • gte - Greater than or equal: |gte=10
  • lt - Less than: |lt=10
  • lte - Less than or equal: |lte=10

Property Filters

  • prop - Get nested property: |prop=user.name
  • len - Get length: |len

Register Custom Filters

import filter from '@adalink/spark-echo/filter';

filter.set('uppercase', (data) => data.toUpperCase());
filter.set('reverse', (data) => data.split('').reverse().join(''));

🎯 Real-World Use Cases

Form Validation

<form>
  <input type="text" id="username" />
  <input type="password" id="password" />
  <my-button on="*change:method/checkValidity"></my-button>
  <my-error on="*invalid:method/showError"></my-error>
</form>
class FormValidator extends Echo(HTMLElement) {
  checkValidity({ detail }) {
    const isValid = this.validate(detail);
    if (!isValid) {
      this.dispatchEvent(new CustomEvent('invalid', {
        detail: { field: detail.field, error: 'Invalid input' }
      }));
    }
  }
}

Data Synchronization

<my-list id="items" data='[1,2,3]'></my-list>
<my-chart on="#items/change:method/update|prop=length"></my-chart>
<my-counter on="#items/change:method/setCount|prop=length"></my-counter>

Parent-Child Communication

<parent-component>
  <child-component on="parent/action:method/childAction"></child-component>
</parent-component>

🎯 Advanced Usage

Multiple Event Arcs

<my-component
  on="*click:method/handleClick|add=1|toString"
  on="*change:attribute/value|prop=data.newValue"
  on="*error:method/handleError">
</my-component>

Filter Chains

<!-- Transform data through multiple filters -->
<my-component on="*data:method/process|add=10|multiply=2|toFixed=2|toString"></my-component>

Cross-Component Communication

<!-- Component A broadcasts events -->
<my-broadcaster id="broadcaster"></my-broadcaster>

<!-- Component B listens to A -->
<my-listener on="#broadcaster/message:method/handleMessage|prop=content"></my-listener>

<!-- Component C also listens to A -->
<my-logger on="#broadcaster/message:method/logMessage|len|toString"></my-logger>

📊 Why Echo Over Alternatives?

| Feature | Echo | Event Bus | Redux | Signals | |---------|------|-----------|-------|---------| | Zero Dependencies | ✅ | ❌ | ❌ | ⚠️ | | Native Web Components | ✅ | ✅ | ⚠️ | ✅ | | Declarative Syntax | ✅ | ❌ | ❌ | ⚠️ | | Built-in Filters | ✅ | ❌ | ❌ | ❌ | | Automatic Cleanup | ✅ | ⚠️ | ✅ | ✅ | | Bundle Size | ~2KB | ~5KB | ~15KB | ~8KB | | Framework Agnostic | ✅ | ✅ | ⚠️ | ✅ |


🌐 Usage in Frameworks

With React

import Echo from '@adalink/spark-echo';

class ReactBridge extends Echo(HTMLElement) {
  connectedCallback() {
    this.addEventListener('react-click', (e) => {
      // Call React component method
      this._reactHandler?.(e.detail);
    });
  }
}

customElements.define('react-bridge', ReactBridge);

// In React
function App() {
  const handleClick = (data) => console.log(data);

  return <react-bridge ref={(el) => el._reactHandler = handleClick} />;
}

With Vue

import Echo from '@adalink/spark-echo';

class VueBridge extends Echo(HTMLElement) {
  connectedCallback() {
    this.addEventListener('vue-event', (e) => {
      // Emit to Vue component
      this._vueComponent?.$emit('vue-event', e.detail);
    });
  }
}

customElements.define('vue-bridge', VueBridge);

🛠️ Development

Prerequisites

  • Node.js 18+

Setup

# Clone repository
git clone https://github.com/Adalink-ai/spark_echo.git
cd spark_echo

# Install dependencies
npm install

# Build package
npm run build

# Start development server
npm run dev

# Lint and format
npm run lint

📚 Documentation


🤝 Contributing

We welcome contributions! Please read our Contributing Guide before getting started.

Ways to contribute:


👥 Author & Community

Cleber de Moraes Goncalves - Creator & Lead Maintainer

🌟 Star the Project

If you find Echo useful, please ⭐ star it on GitHub!

📢 Share

Share Echo with your network:


📄 License

Apache-2.0 © 2026 Adalink


🔗 Links


Built with ❤️ by Adalink

Spark Echo Library - Build reactive web components with event arcs. ⚡