Skip to content

Framework Compatibility

Release@salsafx/uiv16.62.0

SalsaFX components are built on the W3C Web Components standard (Custom Elements v1, Shadow DOM v1). Because they are native browser primitives, they are 100% framework-agnostic and work seamlessly in any front-end environment without proprietary runtime wrappers.

This guide provides dedicated, in-depth integration examples and best practices for each major framework:


Lit is SalsaFX’s native environment. Because SalsaFX components are themselves built with LitElement, integrating them into a Lit application introduces zero runtime overhead.

  • Property Binding: Use .property=${value} syntax in `html``` template tags to pass rich JavaScript values (numbers, objects, booleans) directly to component properties without string serialization.
  • Event Handling: Listen to custom events using @input=${handler} or @change=${handler}. The updated value is dispatched in event.detail.displayValue.
  • Typings: All custom element interfaces and property types are directly available via @salsafx/ui.
import { LitElement, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import '@salsafx/ui';
@customElement('default-page')
export class DefaultPage extends LitElement {
@state() private oilTemp = 45;
render() {
return html`
<fx-card>
<fx-stack-panel align-x="center">
<fx-radial-gauge
.value=${this.oilTemp}
min="0"
max="100"
label="Oil Temperature"
unit="°C"
></fx-radial-gauge>
<fx-fader
.value=${this.oilTemp}
min="0"
max="100"
label="Oil Temperature"
unit="°C"
orientation="horizontal"
value-origin="start"
@input=${(event: CustomEvent<{ displayValue: number }>) => {
this.oilTemp = event.detail.displayValue;
}}
></fx-fader>
</fx-stack-panel>
</fx-card>
`;
}
}

React supports Web Components in two distinct ways. Depending on your project requirements and React version, you can either use our first-class JSX component wrappers or the native custom elements directly.

Option A: Using Typed JSX Wrappers (@salsafx/ui/react)

Section titled “Option A: Using Typed JSX Wrappers (@salsafx/ui/react)”

(Recommended for React 18 & 19)

SalsaFX exports ready-to-use React component wrappers from @salsafx/ui/react (or @salsafx/ui-pro/react in SalsaFX Pro). These provide idiomatic PascalCase components, camelCase props, TypeScript intellisense, and standard React synthetic-style event listeners.

import { useState } from 'react';
import { FxCard, FxStackPanel, FxRadialGauge, FxFader } from '@salsafx/ui/react';
export function ReactDashboard() {
const [oilTemp, setOilTemp] = useState(45);
return (
<FxCard>
<FxStackPanel alignX="center">
<FxRadialGauge
value={oilTemp}
min={0}
max={100}
label="Oil Temperature"
unit="°C"
/>
<FxFader
value={oilTemp}
min={0}
max={100}
label="Oil Temperature"
unit="°C"
orientation="horizontal"
valueOrigin="start"
onInput={(event) => {
setOilTemp(event.detail.displayValue);
}}
/>
</FxStackPanel>
</FxCard>
);
}

Option B: Using Native Custom Elements (<fx-*>)

Section titled “Option B: Using Native Custom Elements (<fx-*>)”

If you prefer not to import the @salsafx/ui/react wrapper package, you can register the custom elements once via import '@salsafx/ui'; and use native dash-separated tags (<fx-card>, <fx-radial-gauge>, <fx-fader>) and kebab-case attributes (align-x, value-origin).

Because SalsaFX controls emit standard DOM events, you can listen to them with regular React JSX callbacks like onInput. React wraps that DOM event in a synthetic event, so the payload is on event.nativeEvent.detail:

import { useState } from 'react';
import '@salsafx/ui';
export function ReactNativeDashboard() {
const [oilTemp, setOilTemp] = useState(45);
return (
<fx-card>
<fx-stack-panel align-x="center">
<fx-radial-gauge
value={oilTemp}
min="0"
max="100"
label="Oil Temperature"
unit="°C"
></fx-radial-gauge>
<fx-fader
value={oilTemp}
min="0"
max="100"
label="Oil Temperature"
unit="°C"
orientation="horizontal"
value-origin="start"
onInput={(event) => {
setOilTemp(event.nativeEvent.detail.displayValue);
}}
></fx-fader>
</fx-stack-panel>
</fx-card>
);
}

SolidJS features fine-grained reactivity and direct DOM compilation, making it one of the fastest and most natural partners for SalsaFX Web Components. You have two integration options.

Option A: Using Typed JSX Wrappers (@salsafx/ui/solid)

Section titled “Option A: Using Typed JSX Wrappers (@salsafx/ui/solid)”

(Recommended)

SalsaFX exports dedicated SolidJS component wrappers from @salsafx/ui/solid. These wrappers are optimized for Solid signals, provide full TypeScript typings, and accept camelCase props.

import { createSignal } from 'solid-js';
import { FxCard, FxStackPanel, FxRadialGauge, FxFader } from '@salsafx/ui/solid';
export function SolidDashboard() {
const [oilTemp, setOilTemp] = createSignal(45);
return (
<FxCard>
<FxStackPanel alignX="center">
<FxRadialGauge
value={oilTemp()}
min={0}
max={100}
label="Oil Temperature"
unit="°C"
/>
<FxFader
value={oilTemp()}
min={0}
max={100}
label="Oil Temperature"
unit="°C"
orientation="horizontal"
valueOrigin="start"
onInput={(event) => {
setOilTemp(event.detail.displayValue);
}}
/>
</FxStackPanel>
</FxCard>
);
}

Option B: Using Native Custom Elements (<fx-*>)

Section titled “Option B: Using Native Custom Elements (<fx-*>)”

SolidJS JSX compiles directly to DOM nodes without a Virtual DOM, which allows you to interact with native <fx-*> elements effortlessly:

  • Use prop:value={oilTemp()} to bind a signal directly to a DOM object property (bypassing attribute stringification).
  • Use on:input={...} to listen to custom DOM events emitted by the element.
import { createSignal } from 'solid-js';
import '@salsafx/ui';
export function SolidNativeDashboard() {
const [oilTemp, setOilTemp] = createSignal(45);
return (
<fx-card>
<fx-stack-panel align-x="center">
<fx-radial-gauge
prop:value={oilTemp()}
min="0"
max="100"
label="Oil Temperature"
unit="°C"
></fx-radial-gauge>
<fx-fader
prop:value={oilTemp()}
min="0"
max="100"
label="Oil Temperature"
unit="°C"
orientation="horizontal"
value-origin="start"
on:input={(event: CustomEvent<{ displayValue: number }>) => {
setOilTemp(event.detail.displayValue);
}}
></fx-fader>
</fx-stack-panel>
</fx-card>
);
}

Vue 3 has 100% compatibility with Custom Elements. It automatically determines whether a binding should be applied as an attribute or as a property and natively supports @custom-event listeners.

  • Import: Register the elements globally or in your entry file using import '@salsafx/ui';.
  • Property Binding: Bind reactive variables using :value="oilTemp".
  • Event Handling: Listen to custom events directly in template with @input="oilTemp = $event.detail.displayValue".

To prevent Vue from warning about unrecognized components, configure isCustomElement in your vite.config.ts:

vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('fx-'),
},
},
}),
],
});
<script setup lang="ts">
import { ref } from 'vue';
import '@salsafx/ui';
const oilTemp = ref(45);
</script>
<template>
<fx-card>
<fx-stack-panel align-x="center">
<fx-radial-gauge
:value="oilTemp"
:min="0"
:max="100"
label="Oil Temperature"
unit="°C"
/>
<fx-fader
:value="oilTemp"
:min="0"
:max="100"
label="Oil Temperature"
unit="°C"
orientation="horizontal"
value-origin="start"
@input="oilTemp = $event.detail.displayValue"
/>
</fx-stack-panel>
</fx-card>
</template>

Svelte compiles directly to vanilla JavaScript without a virtual DOM. Because Svelte elements are real DOM nodes, Web Components are treated as first-class citizens.

  • Import: Import @salsafx/ui in your <script> tag or app layout.
  • Property Binding: Bind state using standard curly braces value={oilTemp}.
  • Event Handling: Listen to custom DOM events using the on:input directive. The payload is accessed via event.detail.displayValue.
<script lang="ts">
import '@salsafx/ui';
let oilTemp = 45;
function handleFaderInput(event: CustomEvent<{ displayValue: number }>) {
oilTemp = event.detail.displayValue;
}
</script>
<fx-card>
<fx-stack-panel align-x="center">
<fx-radial-gauge
value={oilTemp}
min={0}
max={100}
label="Oil Temperature"
unit="°C"
/>
<fx-fader
value={oilTemp}
min={0}
max={100}
label="Oil Temperature"
unit="°C"
orientation="horizontal"
value-origin="start"
on:input={handleFaderInput}
/>
</fx-stack-panel>
</fx-card>