Hi team, I could use some help with
# dev-help
a
Hi team, I could use some help with Liveboard. The issue I’m seeing is that whenever I change any runtime filter, the whole Liveboard gets fully unmounted and rebuilt. The only thing that actually changes is the runtime filter itself. I’ve attached a video where you can see both the behavior and the implementation right away. What I’d expect instead is that changing filters wouldn’t trigger a full re-render with one global spinner, but rather just update the charts individually with smaller loaders for each of them. https://cdn.discordapp.com/attachments/1143209464875462756/1493982731141517432/Apr_6_2026.mov?ex=69e19c17&is=69e04a97&hm=5488c61ad2c71c2e53e957210311b072395398254edad2d5dbdd0d449e4d5411&
s
Hi @Andrii: Here is the Fix: Use HostEvent.UpdateRuntimeFilters via useEmbedRef The correct pattern is to keep runtimeFilters out of the React props entirely and instead imperatively push filter updates using the HostEvent API via a ref. This sends a postMessage to the already-loaded iframe without destroying it, which gives you per-viz loaders instead of the global spinner:
Copy code
import { 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:
Copy code
// ✅ 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);
a
Hi @Sandeep Yadav , I tried this approach but then there was another problem where the initial filters were not applied. To fix this I even added a listener to invoke the filter change. It almost worked but anyway we faced a situation where sometimes no filters were during initialization. https://cdn.discordapp.com/attachments/1493982731820859412/1494618868872904785/thoughtspotlivebard.tsx?ex=69e343cb&is=69e1f24b&hm=47ef0ea26b99e1d9693dd8e565ef32f8f8a6fa7037c4e0bbdd61efc815421d51&
s
Copy code
// 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