Skip to main content
Version: 0.31.0

Drive a template with events

Once a template is running, you can push custom events to it from your backend to change what it shows, such as toggling a live badge or updating a caption, without re-uploading the template.

Handle events in the template​

Inside a template, subscribe to events with the eventBus. Each on call returns an unsubscribe function. Event names and payloads are entirely defined by your application.

import { useEffect, useState } from "react"; import { Text, View } from "@swmansion/smelter"; import { eventBus } from "@fishjam-cloud/composition"; export default function App() { const [caption, setCaption] = useState<string | null>(null); const [live, setLive] = useState(false); useEffect(() => { const unsubs = [ eventBus.on<{ text: string }>("SET_CAPTION", ({ text }) => setCaption(text), ), eventBus.on("CLEAR_CAPTION", () => setCaption(null)), eventBus.on<{ live: boolean }>("SET_LIVE", ({ live }) => setLive(live)), ]; return () => unsubs.forEach((off) => off()); }, []); return ( <View> {live && <Text>● LIVE</Text>} {caption && <Text>{caption}</Text>} </View> ); }

Deploy it​

Now create the composition. A caption-only template registers no inputs, and a composition with no input media cleans itself up after five minutes, so create this one with cleanup_without_inputs set to false:

import { CompositionClient } from "@fishjam-cloud/js-server-sdk"; const compositionClient = new CompositionClient({ managementToken }); const { compositionId } = await compositionClient.createComposition({ cleanupWithoutInputs: false, });
A composition bills for the whole time it exists

With that guard off, cleanup waits for the outputs to fall silent too, so one that keeps publishing is never cleaned up for you. Delete it as soon as you are finished, and do not leave one running after a test. See Cost and lifecycle.

Build the bundle and register it as a templated output with registerTemplateOutput, as in Write and deploy a template.

That output has to publish somewhere, so create a livestream and take its streamer token first, as in Step 3 of the compositions tutorial. Watching it works the same way as there.

Send events from your backend​

Send an event with sendEvent. The event name is matched against your eventBus.on(...) subscriptions, and data is delivered as the handler's argument.

await compositionClient.sendEvent(compositionId, { eventName: "SET_CAPTION", data: { text: "Welcome to the stream" }, });
await compositionClient.sendEvent(compositionId, { eventName: "SET_LIVE", data: { live: true }, });

The template re-renders as soon as the event arrives.

note

Event names and payloads are an application-defined contract between your backend and your template. There is no canonical set of events; SET_LIVE and SET_CAPTION above are just examples. Pick names and payload shapes that fit your app, and keep them in sync on both sides.

Clean up​

Delete the composition when you have finished testing, so it stops billing:

await compositionClient.deleteComposition(compositionId);