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

@reactive-web-components/rwc

v2.64.5

Published

Modern library for creating reactive web components with declarative syntax and strict typing

Downloads

197

Readme

Reactive Web Components (RWC)

🇷🇺 Документация на русском

RWC is a lightweight runtime for building reactive Web Components without depending on a specific framework. It combines fine-grained signals, effects, and a declarative TypeScript-first HTML factory to compose UI from strongly-typed primitives — no templates, no JSX, just TypeScript.

Why RWC

Framework-free signals & effects

Signals and effects in RWC are independent primitives — they work anywhere in your code with zero restrictions. This is a fundamental difference from existing frameworks:

| | Signals anywhere | Effects anywhere | Needs special context | |---|:---:|:---:|:---:| | RWC | ✅ | ✅ | No | | Angular | ✅ | ❌ | effect() requires injection context (constructor, factory, runInInjectionContext) [1][2] | | Solid.js | ✅ | ⚠️ | createEffect needs a reactive owner (createRoot / render / runWithOwner) — effects outside a root will never be disposed [3][4] |

In Angular, calling effect() in ngOnInit, in a regular method, or outside a component throws NG0203. The workaround is to inject an Injector and wrap code in runInInjectionContext. In Solid.js, createEffect outside a tracking scope (e.g. in setTimeout, async code, or global scope) either leaks memory or requires manual getOwner / runWithOwner plumbing.[1][5][2][6][4]

RWC has none of these limitations. Signals and effects are first-class citizens that work in any context — inside components, outside components, in utility functions, in async code, everywhere.[7]

TypeScript all the way down

RWC takes a no-HTML approach: all markup is built through TypeScript factory functions (div, button, input, …). This means:[7]

  • Full type-checking in templates — props, attributes, events, slots, and children are all typed.
  • Autocomplete everywhere — IDE suggestions for every config option, event name, and attribute.
  • Compile-time errors — typos in attribute names or wrong event handler signatures are caught before runtime.

Typed slots

Slots in RWC are fully typed via slotTemplate. The parent component defines what templates it expects, and the child component consumes them with typed context — similar to scoped slots in Vue, but with compile-time guarantees.[7]

Features

  • Reactivitysignal, effect, createSignal, rs, computed, pipe, forkJoin, combineLatest.[7]
  • Class components — decorators (@component, @property, @event) with lifecycle hooks.[7]
  • Functional components — lightweight alternative via createComponent.[7]
  • HTML factory — declarative element creation with typed config (ComponentInitConfig).[7]
  • Shorthand config.attr for attributes, @event for listeners, $name for effects.[7]
  • Control flowgetList (keyed efficient lists), when (conditional render), show (CSS toggle).[7]
  • Slots — typed slotTemplate with scoped context.[7]
  • DI & styling — context via providers/injects, reactive refs, reactive classList and style.[7]

Installation

npm install @reactive-web-components/rwc

Requires TypeScript and a modern browser with Custom Elements support.

Quick Start

A minimal reactive counter using shorthand config syntax:

import { component, property, event } from '@reactive-web-components/rwc';
import { BaseElement } from '@reactive-web-components/rwc';
import { div, button } from '@reactive-web-components/rwc';
import { signal, rs, newEventEmitter, useCustomComponent } from '@reactive-web-components/rwc';

@component('rwc-counter')
class Counter extends BaseElement {
  @property()
  count = signal(0);

  @event()
  onCountChange = newEventEmitter<number>();

  render() {
    return div(
      button(
        {
          "@click": () => {
            this.count.update((v) => v + 1);
            this.onCountChange(this.count());
          },
        },
        rs`Count: ${this.count}`, // using rs`...`
        () => `Count: ${this.count()}`, // using function with signall call
      );
    );
  }
}

export const CounterComp = useCustomComponent(Counter);

Usage in another component:

CounterComp({
  '.count': signal(10),                    // typed attribute via shorthand
  '@onCountChange': (v) => console.log(v), // typed event via shorthand
})

Or directly in HTML:

<rwc-counter></rwc-counter>

Config Shorthand Syntax

RWC supports a concise config notation alongside the standard one:[7]

// Shorthand — less boilerplate, same type safety
div({
  '.id': 'main',
  '.tabIndex': 0,
  '@click': (e) => console.log('clicked', e),
  '$onMount': (el) => console.log('element created', el),
}, 'Content')

// Equivalent standard config
div({
  attributes: { id: 'main', tabIndex: 0 },
  listeners: { click: (e) => console.log('clicked', e) },
  effects: [(el) => console.log('element created', el)],
}, 'Content')

| Prefix | Meaning | Example | |--------|---------|---------| | . | Attribute / property | '.disabled': true | | @ | Event listener (DOM or custom) | '@click': handler | | $ | Effect (runs on element creation) | '$init': (el) => ... |

Typed Slot Templates

@component('item-list')
class ItemList extends BaseElement {
  public slotTemplate = defineSlotTemplate<{
    item: (ctx: { id: number; name: string }) => ComponentConfig<any>;
  }>();

  @property()
  items = signal<{ id: number; name: string }[]>([]);

  render() {
    return div(getList(
      this.items,
      (item) => item.id,
      (item) => this.slotTemplate.item?.(item) || div(item.name)
    ));
  }
}

export const ItemListComp = useCustomComponent(ItemList);

// Consumer — fully typed context in the slot
ItemListComp({ '.items': data })
  .setSlotTemplate({
    item: (ctx) => div(`${ctx.name} (#${ctx.id})`), // ctx is typed!
  })

When to Use RWC

  • Low-level but type-safe layer for Web Components without a heavy framework.
  • Signal-based reactivity (like Solid or Angular Signals) on top of the native DOM — but without their context restrictions.[8][6]
  • Shared runtime across projects — vanilla apps, microfrontends, or integration into Angular/React via wrappers.
  • Compile-time safety in templates, slots, and event handlers.

Documentation

| Resource | Description | |----------|-------------| | docs.ru.md | Full documentation in Russian — complete API reference with examples | | docs.en.md | Full documentation in English — complete API reference with examples | | src/ | Source code and usage examples |

Project Status

The library is under active development. The core API is stable and used in production prototypes, but minor changes to typings and helper utilities may still occur.[7]

Contributions, issues, and pull requests are welcome!

License

MIT


References

  1. Signals in Angular: Building Blocks - Several building blocks for Signals such as effect can only be used in an injection context. This is...

  2. Side effects for non-reactives APIs - Injection context link. By default, you can only create an effect() within an injection context (whe...

  3. runWithOwner - Execute code under a specific owner in SolidJS for proper cleanup and context access, especially in ...

  4. Using signal outside of component. · solidjs solid · Discussion #397 - The basic of it is while it isn't restricted to components, Solid's reactivity is built with framewo...

  5. Effects and InjectionContext in Angular(v21) - DEV Community - Mastering the Life of an Effect: Injection Context and Beyond To understand why an...

  6. SolidJS: "computations created outside a createRoot or render will never be disposed" messages in the console log - When working on a SolidJS project you might start seeing the following warning message in your JS co...

  7. reactive-web-components/rwc 2.51.8 on npm - Modern library for creating reactive web components with declarative syntax and strict typing

  8. effect() should have an option to run outside of an injection ... - Which @angular/* package(s) are relevant/related to the feature request? core Description Angular's ...