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:
- Typescript
- Python
- curl
import {CompositionClient } from "@fishjam-cloud/js-server-sdk"; constcompositionClient = newCompositionClient ({managementToken }); const {compositionId } = awaitcompositionClient .createComposition ({cleanupWithoutInputs : false, });
from fishjam import CompositionClient from fishjam.composition import CreateCompositionRequest composition_client = CompositionClient(management_token=management_token) composition = composition_client.create_composition( CreateCompositionRequest(cleanup_without_inputs=False), ) composition_id = composition.composition_id
export TOKEN="<YOUR_MANAGEMENT_TOKEN>" curl -X POST "https://rtc.fishjam.io/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 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.
- Typescript
- Python
- curl
awaitcompositionClient .sendEvent (compositionId , {eventName : "SET_CAPTION",data : {text : "Welcome to the stream" }, });
composition_client.send_event(composition_id, "SET_CAPTION", {"text": "Welcome to the stream"})
curl -X POST "https://rtc.fishjam.io/api/composition/$COMPOSITION/event" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "event_name": "SET_CAPTION", "data": { "text": "Welcome to the stream" } }'
- Typescript
- Python
- curl
awaitcompositionClient .sendEvent (compositionId , {eventName : "SET_LIVE",data : {live : true }, });
composition_client.send_event(composition_id, "SET_LIVE", {"live": True})
curl -X POST "https://rtc.fishjam.io/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:
- Typescript
- Python
- curl
awaitcompositionClient .deleteComposition (compositionId );
composition_client.delete_composition(composition_id)
curl -X DELETE "https://rtc.fishjam.io/api/composition/$COMPOSITION" \ -H "Authorization: Bearer $TOKEN"