Appearance
Framework Guides
Pulse uses standard web components — they work in every framework. Here's how to integrate in yours.
React
tsx
import '@gamention/pulse-elements';
import { useEffect, useState } from 'react';
function App() {
const [token, setToken] = useState<string | null>(null);
useEffect(() => {
fetch('/api/collab-token')
.then(res => res.json())
.then(data => setToken(data.token));
}, []);
if (!token) return <div>Loading...</div>;
return (
<div>
<h1>Dashboard</h1>
{/* @ts-ignore — React doesn't know about custom elements yet */}
<pulse-widget
api-key="pk_your_key"
token={token}
room="dashboard"
endpoint="wss://pulse.hire.rest"
/>
</div>
);
}React & Custom Elements
React 19+ fully supports custom element properties. For React 18, string attributes work fine — Pulse accepts all configuration via attributes.
Vue
vue
<template>
<div>
<h1>Dashboard</h1>
<pulse-widget
v-if="token"
:api-key="apiKey"
:token="token"
room="dashboard"
endpoint="wss://pulse.hire.rest"
/>
</div>
</template>
<script setup lang="ts">
import '@gamention/pulse-elements';
import { ref, onMounted } from 'vue';
const apiKey = 'pk_your_key';
const token = ref<string | null>(null);
onMounted(async () => {
const res = await fetch('/api/collab-token');
const data = await res.json();
token.value = data.token;
});
</script>Tell Vue to skip Pulse elements in vite.config.ts:
typescript
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('pulse-')
}
}
})
]
});Svelte
svelte
<script lang="ts">
import '@gamention/pulse-elements';
import { onMount } from 'svelte';
let token: string | null = null;
onMount(async () => {
const res = await fetch('/api/collab-token');
const data = await res.json();
token = data.token;
});
</script>
<h1>Dashboard</h1>
{#if token}
<pulse-widget
api-key="pk_your_key"
{token}
room="dashboard"
endpoint="wss://pulse.hire.rest"
/>
{/if}Angular
Add CUSTOM_ELEMENTS_SCHEMA to your module:
typescript
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
@NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA] })
export class AppModule {}typescript
import '@gamention/pulse-elements';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>Dashboard</h1>
<pulse-widget
*ngIf="token"
[attr.api-key]="apiKey"
[attr.token]="token"
room="dashboard"
endpoint="wss://pulse.hire.rest"
></pulse-widget>
`
})
export class AppComponent implements OnInit {
apiKey = 'pk_your_key';
token: string | null = null;
async ngOnInit() {
const res = await fetch('/api/collab-token');
const data = await res.json();
this.token = data.token;
}
}Next.js
tsx
'use client';
import { useEffect, useState } from 'react';
export default function Page() {
const [token, setToken] = useState<string | null>(null);
useEffect(() => {
// Import client-side only (web components can't run on the server)
import('@gamention/pulse-elements');
fetch('/api/collab-token')
.then(res => res.json())
.then(data => setToken(data.token));
}, []);
if (!token) return null;
return (
<div>
<h1>Dashboard</h1>
{/* @ts-ignore */}
<pulse-widget
api-key="pk_your_key"
token={token}
room="dashboard"
endpoint="wss://pulse.hire.rest"
/>
</div>
);
}TIP
In Next.js (or any SSR framework), import @gamention/pulse-elements inside useEffect since web components require the browser DOM.
Changing Rooms
All frameworks support dynamic room changes. Update the room attribute when the user navigates:
typescript
// The widget automatically disconnects from the old room
// and reconnects to the new one
widget.setAttribute('room', newPageId);