GitHub logo
JavaScript library for component-based websites and applications.

Using with Frameworks

Because zuix.js works directly with standard HTML, CSS, and JavaScript without requiring a custom Virtual DOM or a proprietary build pipeline, it is inherently framework-agnostic.

While mixing different framework logic on the same DOM element is generally a bad practice, there is a modern, highly scalable architecture where integrating zuix.js with frameworks like React, Angular, or Vue makes perfect sense: Web Components (Custom Elements).

This approach is perfectly aligned with the philosophy of Micro-frontends and framework-agnostic Design Systems.

The Invisible Boundary

When you package your zuix.js component as a standard HTML5 Custom Element (e.g., <counter-widget>), the browser creates a "black box" (often isolated via Shadow DOM):

Communication across this boundary happens through standard Web interfaces:

  1. Inputs (Framework ➔ Zuix): Calling public methods exposed by the zuix component.
  2. Outputs (Zuix ➔ Framework): Listening to standard Custom Events triggered by the zuix component.

1. Preparing the zuix.js Component

To allow external frameworks to interact with your component, you need to define a Public API using the this.expose(name, function) method, and emit events using this.trigger(eventName, data).

Let's assume we have packaged a component registered as the <counter-widget> Custom Element. Here is its internal controller:

counter-widget.js

class CounterWidget extends ControllerInstance {

  onInit() {
    // Expose public methods to the outside world (React, Angular, etc.)
    this.expose('setValue', (v) => this.updateCounter(v));
    this.expose('reset', () => this.updateCounter(0));
  }

  onCreate() {
    this.counter = 0;
    
    // Bind an internal button click
    this.view().find('button').on('click', () => {
      this.updateCounter(this.counter + 1);
    });
  }

  updateCounter(value) {
    this.counter = value;
    this.view().find('.display').html(this.counter);
    
    // Emit a CustomEvent to notify the host framework
    this.trigger('counterChanged', { newValue: this.counter });
  }
}

2. Accessing the Component Interface

The cleanest and most idiomatic way to access a component's context in zuix.js is to assign it an explicit name using the z-context attribute directly on the HTML tag.

<counter-widget z-context="my-counter"></counter-widget>

By doing this, you don't need to query the DOM or use framework-specific element references. You can just ask zuix.js for that context by its name from anywhere in your code:

// Fetch the context by its z-context name and call the exposed method
zuix.context('my-counter', (ctx) => {
  // 'ctx' is the component context. 
  ctx.reset(); 
  ctx.setValue(42);
});

Note: zuix.context() accepts a callback because the component initialization might be asynchronous.

3. Integration Examples

Here is how you can mount, listen to, and control our <counter-widget> inside the three most popular frontend frameworks. We use the z-context name to grab a reference and the optional chaining operator (?.) to call its methods cleanly.

import React, { useEffect, useState } from 'react';

export default function ReactHostApp() {
  const [val, setVal] = useState(0);
  let myCounter; // Cached reference

  useEffect(() => {
    window.zuix.context('my-counter', (ctx) => { myCounter = ctx; });
    const el = document.querySelector('[z-context="my-counter"]');
    const handler = (e) => setVal(e.detail.newValue);
    el.addEventListener('counterChanged', handler);
    return () => el.removeEventListener('counterChanged', handler);
  }, []);

  return (
    <div>
      <h3>React State: {val}</h3>
      <counter-widget z-context="my-counter"></counter-widget>
      <button onClick={() => myCounter?.reset()}>Reset</button>
    </div>
  );
}
@Component({ ... })
export class AppComponent implements OnInit {
  currentValue = 0;
  private myCounter: any;

  ngOnInit() {
    window.zuix.context('my-counter', (ctx) => { this.myCounter = ctx; });
  }

  onCounterChange(e: any) {
    this.currentValue = e.detail.newValue;
  }

  resetWidget() {
    this.myCounter?.reset();
  }
}
<template>
  <h3>Vue State: </h3>
  <counter-widget z-context="my-counter" @counterChanged="onCounterChange"></counter-widget>
  <button @click="myCounter?.reset()">Reset</button>
</template>

<script>
export default {
  data() { return { currentValue: 0, myCounter: null }; },
  mounted() {
    window.zuix.context('my-counter', (ctx) => { this.myCounter = ctx; });
  },
  methods: {
    onCounterChange(e) { this.currentValue = e.detail.newValue; }
  }
};
</script>

Summary

By combining z-context identification with the ?. optional chaining operator, you achieve a clean, decoupled architecture. zuix.js components remain true, independent black-boxes, providing a robust and framework-agnostic way to share UI logic across any modern frontend stack.

The Controller
CLI / Templates

Content Index

📖

🕵🏻 Explore zuix.js DeepWiki

Technical Docs & Interactive Wiki

Ask the AI 🧠