Catalyst

GitHub's lightweight framework of decorators and lifecycle hooks for building well-structured, testable Web Components.

Framework
npm
v1.8.1
1,431stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
76/100Good
Development Activity72
Maintenance60
Community72
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
85/100Excellent
Architecture82
Code Quality88
Innovation78
Learning Curve90

Catalyst is the framework GitHub’s own frontend team built to bring structure to native Web Components, distilling years of jQuery-era patterns — event delegation, DOM observation, and selector-based querying — into a small set of decorators and lifecycle hooks. Rather than replacing the platform, it wraps HTMLElement subclasses with a @controller decorator that manages custom element registration, connectedCallback/disconnectedCallback wiring, and attribute change handling, so every component in an application follows the same predictable structure.

On top of that base, Catalyst layers @attr for typed data-attribute-backed properties, @target/@targets for querying child elements by role, data-action bindings that connect DOM events to controller methods, and a context system (@provide/@consume) for passing values down a component tree without prop drilling. The result is a set of small, composable ‘abilities’ (mixins) that let teams write many Web Components with consistent conventions and strong test coverage, instead of each developer inventing their own patterns.

What You Get

  • A @controller decorator that auto-registers custom elements and wires up connected/disconnected/attribute-changed lifecycle callbacks
  • @attr decorators that expose typed, data-attribute-backed properties on your element
  • @target/@targets decorators for finding scoped child elements without manual querySelector calls
  • data-action event binding that connects DOM events directly to controller methods, including within shadow DOM
  • A @provide/@consume context API for passing values down the DOM tree, following the community Context Protocol
  • A lazyDefine helper for deferring custom element registration until an element is visible, interacted with, or the page is ready

Common Use Cases

  • Migrating large jQuery-driven frontends to native Web Components incrementally, component by component
  • Building progressive-enhancement UI where server-rendered HTML is enhanced by small, testable controllers
  • Sharing state between a parent controller and nested Web Components without manual prop passing
  • Deferring the cost of defining rarely-used custom elements until they scroll into view or the user interacts with the page
  • Writing consistent, unit-testable UI behavior across many independent frontend teams inside one organization

Under The Hood

Architecture Catalyst is a modular set of independent decorator and utility modules — core.ts, controller.ts, attr.ts, target.ts, bind.ts, providable.ts, ability.ts, mark.ts, lazy-define.ts — composed around a shared per-prototype metadata registry (the meta() helper in core.ts). CatalystDelegate is the central orchestration point: it wraps the target class’s connectedCallback, disconnectedCallback, and attributeChangedCallback, and redefines observedAttributes as a getter, delegating attribute initialization, event binding, shadow-root setup, and lazy-load observation in a fixed order. Decorators like @attr, @target, and @controller are thin — they register metadata and define per-property getters, deferring actual behavior to runtime helper functions rather than baking logic into the decorators themselves. The abilities pattern (ability.ts/mark.ts) generalizes this into a reusable mixin-composition primitive that Catalyst uses internally to implement its own context system, and exposes publicly so consumers can build the same kind of composable behavior. Because every decorator reads and writes through this shared registry and lifecycle, changing CatalystDelegate’s core behavior would ripple through the whole public API.

Tech Stack Written in strict-mode TypeScript targeting a modern ECMAScript baseline, with legacy experimental decorators enabled for its decorator API. There are no runtime dependencies — the library builds directly on native platform APIs (MutationObserver, IntersectionObserver, customElements, ShadowRoot). The build pipeline compiles with tsc, lints with an actively maintained shared ESLint configuration layered with stricter import/type-import rules, and tests run in a real browser context via a dev-server-based test runner paired with fixture and spy/fake utilities. Continuous integration runs the test suite, linting, and a Lighthouse performance budget check on every push, and bundle size is guarded by an explicit size limit on the core exports. A companion static documentation site is built separately from the library itself.

Code Quality The project pairs almost every source module with a corresponding browser-run test file, using fixtures and spies to assert on real DOM behavior rather than mocked internals. Strict TypeScript is enforced project-wide, and the shared lint configuration catches common web-platform footguns in addition to standard style issues. Public decorators carry doc-comments explaining exact behavior and edge cases — for example, how native class-field semantics interact with defineProperty timing. Failures are surfaced by throwing synchronously on misuse or by TypeScript’s type system rather than being silently swallowed, and this discipline is enforced continuously in CI rather than left to manual review.

API Design The public surface is small and consistently named — attr, target, targets, controller, bind, provide, consume, lazyDefine — all exported from one entry point, so a team can adopt a single primitive without learning the whole library. Getting started needs minimal boilerplate: a class extends HTMLElement, gets @controller applied, and immediately gains lifecycle wiring, attribute typing, and DOM binding with no separate registration step. Documentation is unusually thorough for a project of this scope — an extensive guide site walks through concepts, decorators, patterns, anti-patterns, and testing, and every decorator carries inline documentation of its exact contract. The main ergonomic friction is its reliance on the legacy experimentalDecorators compiler flag, which requires TypeScript-specific configuration and predates the now-finalized standard decorators proposal.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search