Framework Integration
The <cs-voice-survey> custom element works in any framework. Here are integration guides for the most popular ones.
Vanilla HTML (CDN)
The simplest integration — no build step required.
<!doctype html>
<html>
<head>
<script src="https://cdn.dev.canaryspeech.com/v/latest/voice-survey.min.js"></script>
</head>
<body>
<cs-voice-survey
base-url="https://rest.eus.canaryspeech.com"
access-token="eyJ..."
project-id="proj_abc123"
subject-id="user_xyz"
></cs-voice-survey>
<script>
document.querySelector("cs-voice-survey").addEventListener("completed", (e) => {
console.log("Done!", e.detail);
});
</script>
</body>
</html>
React 18
React 18 does not forward custom element events through its synthetic event system. Attach listeners via useRef + useEffect.
import { useEffect, useRef } from "react";
import "cs-voice-survey-component";
export function VoiceSurveyWrapper() {
const ref = useRef<HTMLElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const onCompleted = (e: Event) => {
const { assessmentId, scores } = (e as CustomEvent).detail;
console.log("Completed:", assessmentId, scores);
};
el.addEventListener("completed", onCompleted);
return () => el.removeEventListener("completed", onCompleted);
}, []);
return (
<cs-voice-survey
ref={ref}
base-url="https://rest.eus.canaryspeech.com"
access-token="eyJ..."
project-id="proj_abc123"
subject-id="user_xyz"
/>
);
}
React 19+
React 19 added native custom element support — events and properties work without wrappers.
import "cs-voice-survey-component";
export function VoiceSurveyWrapper() {
return (
<cs-voice-survey
base-url="https://rest.eus.canaryspeech.com"
access-token="eyJ..."
project-id="proj_abc123"
subject-id="user_xyz"
oncompleted={(e) => console.log("Done!", e.detail)}
/>
);
}
SvelteKit
<script lang="ts">
import "cs-voice-survey-component";
function handleCompleted(e: CustomEvent) {
console.log("Completed:", e.detail);
}
</script>
<cs-voice-survey
base-url="https://rest.eus.canaryspeech.com"
access-token="eyJ..."
project-id="proj_abc123"
subject-id="user_xyz"
on:completed={handleCompleted}
></cs-voice-survey>
Vue 3
<template>
<cs-voice-survey
base-url="https://rest.eus.canaryspeech.com"
access-token="eyJ..."
project-id="proj_abc123"
subject-id="user_xyz"
@completed="handleCompleted"
/>
</template>
<script setup lang="ts">
import "cs-voice-survey-component";
function handleCompleted(e: CustomEvent) {
console.log("Completed:", e.detail);
}
</script>
In vite.config.ts, tell Vue to treat cs-voice-survey as a custom element:
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag === "cs-voice-survey",
},
},
}),
],
});
Angular
Add CUSTOM_ELEMENTS_SCHEMA to your module or standalone component:
import { CUSTOM_ELEMENTS_SCHEMA, Component } from "@angular/core";
import "cs-voice-survey-component";
@Component({
selector: "app-survey",
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<cs-voice-survey
base-url="https://rest.eus.canaryspeech.com"
access-token="eyJ..."
project-id="proj_abc123"
subject-id="user_xyz"
(completed)="onCompleted($event)"
></cs-voice-survey>
`,
})
export class SurveyComponent {
onCompleted(e: CustomEvent) {
console.log("Completed:", e.detail);
}
}
Aurelia 2
Aurelia treats unknown elements as custom elements automatically.
<cs-voice-survey
base-url="https://rest.eus.canaryspeech.com"
access-token.bind="accessToken"
project-code.bind="projectCode"
subject-name.bind="subjectName"
@completed.trigger="onCompleted($event)"
></cs-voice-survey>
Use
.trigger(not.delegate) for custom events from web components.
Alpine.js
<div x-data="{ init() {
this.$refs.survey.addEventListener('completed', (e) => {
console.log('Scores:', e.detail.scores);
});
}
}">
<cs-voice-survey
x-ref="survey"
base-url="https://rest.eus.canaryspeech.com"
:access-token="accessToken"
:project-code="projectCode"
:subject-name="subjectName"
></cs-voice-survey>
</div>