CxJS

Custom Components

import { Widget } from 'cx/ui'; Copied

CxJS includes many built-in components, but you can create custom ones when needed. Custom components extend base classes like Widget, HtmlElement, Field, or PureContainer.

Example

This example creates a Square component with bindable color properties. Click the square to set a random color.

Square.tsx - The widget class (uses React JSX):

/** @jsxImportSource react */

import { RenderingContext, Instance, Widget } from "cx/ui";
import type { NumberProp, WidgetConfig } from "cx/ui";

export interface SquareConfig extends WidgetConfig {
  red?: NumberProp;
  green?: NumberProp;
  blue?: NumberProp;
}

export class Square extends Widget<SquareConfig> {
  declare red: number;
  declare green: number;
  declare blue: number;

  declareData(...args: Record<string, any>[]) {
    super.declareData(...args, {
      red: undefined,
      green: undefined,
      blue: undefined,
    });
  }

  setRandomColor(instance: Instance) {
    instance.set("red", Math.floor(Math.random() * 256));
    instance.set("green", Math.floor(Math.random() * 256));
    instance.set("blue", Math.floor(Math.random() * 256));
  }

  render(context: RenderingContext, instance: Instance, key: string) {
    const { data } = instance;
    return (
      <div
        key={key}
        style={{
          width: 100,
          height: 100,
          backgroundColor: `rgb(${data.red}, ${data.green}, ${data.blue})`,
          cursor: "pointer",
          borderRadius: 4,
        }}
        onClick={() => this.setRandomColor(instance)}
      />
    );
  }
}

Square.prototype.red = 0;
Square.prototype.green = 0;
Square.prototype.blue = 0;

Usage - CxJS application code:

<div class="flex gap-8 items-center">
  <LabelsLeftLayout>
    <Slider label="Red" value={bind(m.red, 0)} minValue={0} maxValue={255} />
    <Slider
      label="Green"
      value={bind(m.green, 0)}
      minValue={0}
      maxValue={255}
    />
    <Slider label="Blue" value={bind(m.blue, 0)} minValue={0} maxValue={255} />
  </LabelsLeftLayout>
  <Square red={m.red} green={m.green} blue={m.blue} />
</div>
 
 
 

React JSX Pragma

Widget files must use the React JSX pragma because the render method returns React elements:

/** @jsxImportSource react */

Config Interface

Define a config interface for your component’s properties. Use prop types like StringProp, NumberProp, BooleanProp for bindable properties:

interface SquareConfig extends WidgetConfig {
  red?: NumberProp;
  green?: NumberProp;
  blue?: NumberProp;
}

Widget Class

Extend Widget (or another base class) with your config type. Use declare for properties to avoid overwriting config values:

class Square extends Widget<SquareConfig> {
  declare red: number;
  declare green: number;
  declare blue: number;
}

Widget Methods

declareData

Register bindable properties. Properties declared here can use data binding:

declareData(...args) {
   super.declareData(...args, {
      red: undefined,
      green: undefined,
      blue: undefined,
   });
}

Use { structured: true } for object properties with bindable sub-properties.

render

Returns React elements for the component’s visual representation:

render(context: RenderingContext, instance: Instance, key: string) {
   const { data } = instance;
   return (
      <div key={key} style={{ backgroundColor: `rgb(${data.red}, ${data.green}, ${data.blue})` }} />
   );
}

Parameters:

  • context - Passes information between parent and child widgets
  • instance - Contains data, store, and instance-specific properties
  • key - Unique identifier for React reconciliation

init

Called once when the widget class is initialized, before any rendering.

initInstance

Called for each widget instance. Use this to set up instance-specific data:

initInstance(context: RenderingContext, instance: Instance) {
   instance.customData = {};
   super.initInstance(context, instance);
}

initState

Sets the initial internal state for stateful components.

explore

Evaluates data-bound attributes and explores children. Called before prepare:

explore(context: RenderingContext, instance: Instance) {
   super.explore(context, instance);
   // Access instance.data here
}

prepare

Additional preparation after explore, before render. Use to get information from context.

cleanup

Called after rendering to perform cleanup work.

Prototype Defaults

Set default property values on the prototype:

Square.prototype.red = 0;
Square.prototype.green = 0;
Square.prototype.blue = 0;

Base Classes

Base ClassUse Case
WidgetBasic widgets
HtmlElementWidgets rendering HTML elements with styling support
FieldForm input widgets with validation
PureContainerContainers without HTML wrapper
ContainerContainers with HTML wrapper