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 functionApp () { const [caption ,setCaption ] =useState <string | null>(null); const [live ,setLive ] =useState (false);useEffect (() => { constunsubs = [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:
export COMPOSITION_URL="https://rtc.fishjam.io" export TOKEN="<YOUR_MANAGEMENT_TOKEN>" curl -X POST "$COMPOSITION_URL/api/composition" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "cleanup_without_inputs": false }'
Save the id from the response:
export COMPOSITION="<COMPOSITION_ID>"
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, the same single multipart request to POST …/output/{output_id}/template 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 POST /api/composition/{composition_id}/event. The event_name is matched against your eventBus.on(...) subscriptions, and data is delivered as the handler's argument.
curl -X POST "$COMPOSITION_URL/api/composition/$COMPOSITION/event" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "event_name": "SET_CAPTION", "data": { "text": "Welcome to the stream" } }'
curl -X POST "$COMPOSITION_URL/api/composition/$COMPOSITION/event" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "event_name": "SET_LIVE", "data": { "live": true } }'
The template re-renders as soon as the event arrives.
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:
curl -X DELETE "$COMPOSITION_URL/api/composition/$COMPOSITION" \ -H "Authorization: Bearer $TOKEN"