ProjectEvergreen / ProjectEvergreen/wcc

support depthful reactivity for JSX + Signals and efficient effects grouping (MVP)

Open
#256 0 comments 0 reactions 1 assignee View on GitHub

@thescientist13 is already working on this.

Since Apr 11, 2026.

documentation feature InferredObservability JSX
Dominant language
JavaScript
Stars
126
Forks
17
PR merge metrics
No merged PRs in 30d

Description

Current State

Currently, WCC's JSX + Signals integration (inferredObservability) does not support reactivity deeper in the first level of a component's JSX tree. So for example, in this example

export const inferredObservability = true;

export default class Counter extends HTMLElement {
  count;
  parity;

  constructor() {
    super();
    this.count = new Signal.State(0);
    this.parity = new Signal.Computed(() => (this.count.get() % 2 === 0 ? 'even' : 'odd'));
  }

  connectedCallback() {
    this.render();
  }

  render() {
    const { count, parity } = this;

    return (
      <div>
        <span class={parity.get()}>
          You have clicked
          <span>{count.get()}</span>
          times
        </span>
      </div>
    );
  }
}

customElements.define('wcc-counter-tsx', Counter);

The nested <span>{count.get()}</span> would not be reactive and effects, templates, etc created for it.

Desired State

The current state was considered a reasonable state as far as a prototype, and so he desired state is that WCC can handle any depth of signals in a template.

Additional things to check:

  • reactive text nodes (at all levels)
  • I think we are only checking for State, I think we also need to handle Computeds (by themselves) here as well
  • should we filter the things that call .get() out against actual signals found in the constructor / JSX?
  • inline version breaks with effects
  • Better data structures for holding onto data within jsx-loader (refactor)
Additional Context

Additionally, taking the counter example from the sandbox page, we should / could improve the number of effects WCC outputs. For example, the counter example currently produces 4 effects, one for every textContent / setAttribute change.

this.$eff0 = effect(() => {
  this.$el0.textContent = SignalCounter.$$tmpl0(this.count.get(), this.parity.get());
});
this.$eff1 = effect(() => {
  this.$el0.setAttribute('class', this.parity.get());
});
this.$eff2 = effect(() => {
  this.$el1.textContent = SignalCounter.$$tmpl1(this.isLarge.get());
});
this.$eff3 = effect(() => {
  this.$el1.setAttribute('data-count', this.count.get());
});

But in reality, since all these signals / computeds are related, it would stand to reason that in this case, only one total effect would be needed?

this.$eff0 = effect(() => {
  this.$el0.textContent = SignalCounter.$$tmpl0(this.count.get(), this.parity.get());
  this.$el0.setAttribute('class', this.parity.get());
  this.$el1.textContent = SignalCounter.$$tmpl1(this.isLarge.get());
  this.$el1.setAttribute('data-count', this.count.get());
});

A post MVP enhancement would probably to start learning how effect works better and start exploring batching in the signal graph.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.