Andrii
04/15/2026, 2:34 PMSandeep Yadav
04/16/2026, 12:38 PMimport { useRef } from 'react';
import {
LiveboardEmbed,
HostEvent,
useEmbedRef,
} from '@thoughtspot/visual-embed-sdk/react';
function MyLiveboard() {
const embedRef = useEmbedRef<typeof LiveboardEmbed>();
const handleFilterChange = (newFilters) => {
// :white_check_mark: Pushes filters into the existing iframe via postMessage
// Does NOT destroy/rebuild the embed
embedRef.current?.trigger(HostEvent.UpdateRuntimeFilters, newFilters);
};
return (
<>
<MyFilterUI onChange={handleFilterChange} />
<LiveboardEmbed
ref={embedRef}
liveboardId="your-liveboard-id"
// :x: DO NOT put runtimeFilters here if they will change
// runtimeFilters={filters} ← this is the not correct
/>
</>
);
}
The HostEvent.UpdateRuntimeFilters postMessage is handled on the ThoughtSpot side by useFilterEmbedEvents / updateEmbedFilters [3], which applies the filter change in-place and only re-fetches the affected viz queries, resulting in individual per-chart loaders.
One More Tip: Initial Filters Are Fine as Props
If you have default/initial filters that don't change, it's perfectly fine to put them in runtimeFilters as a prop. The problem only occurs when you change them after mount. The pattern is:
// ✅ OK: initial filters in props (set once, never changed)
<LiveboardEmbed
ref={embedRef}
liveboardId="..."
runtimeFilters={initialFilters} // static, never changes
/>
// ✅ OK: dynamic filter changes via HostEvent (no re-mount)
embedRef.current?.trigger(HostEvent.UpdateRuntimeFilters, updatedFilters);Andrii
04/17/2026, 8:41 AMSandeep Yadav
04/20/2026, 10:11 AM// Instantiate class for embedding a Liveboard
const embed = new LiveboardEmbed("#your-own-div", {
liveboardId: "9bd202f5-d431-44bf-9a07-b4f7be372125",
runtimeFilters: [
{
columnName: "color",
operator: RuntimeFilterOp.EQ,
values: ["red"]
}
],
});
// Add button and click behavior to update runtime filter from red -> blue without full page refresh
const setBlueBtn = document.createElement("button");
setBlueBtn.textContent = "Set color = blue";
setBlueBtn.style.margin = "12px 0";
const container = document.querySelector("#your-own-div");
if (container && container.parentElement) {
container.parentElement.insertBefore(setBlueBtn, container);
}
setBlueBtn.addEventListener("click", () => {
embed.trigger(HostEvent.UpdateRuntimeFilters, [
{
columnName: "color",
operator: RuntimeFilterOp.EQ,
values: ["blue"]
}
]);
});
Could you please try the above code. If we remove the runtimeFilters in the LiveboardEmbed, it works then as well