The push API lets you imperatively add, remove, and update data on a chart without re-rendering the entire component. This is the foundation for live dashboards, streaming data, interactive editors, and any scenario where data changes after initial render.
Core Methods
JS
const ref = useRef() ref.current.push({ id: "p1", x: 10, y: 20 }) // add one ref.current.pushMany([...points]) // add batch ref.current.remove("p1") // remove by ID ref.current.remove(["p1", "p2"]) // remove batch ref.current.update("p1", d => ({ ...d, y: 99 })) // update in place ref.current.clear() // reset ref.current.getData() // read current data
remove() and update() require an ID accessor so the store can find the item to modify. XY charts use pointIdAccessor, ordinal charts use dataIdAccessor. Geo charts implementupdate() as remove + push internally.
Push + Remove: Scatterplot
When to use: Live sensor feeds, real-time dashboards, any scenario where data points arrive and expire. Push adds a point to the chart instantly. Remove takes it off by ID — no full re-render, no state management.
Click "Push Point" to add random points. "Remove Last" deletes the most recent by ID. The chart re-renders only the affected data — the pipeline recomputes extents and rebuilds the scene incrementally.
JSX
const ref = useRef() <Scatterplot ref={ref} xAccessor="x" yAccessor="y" pointIdAccessor="id" // required for remove/update enableHover tooltip /> // Add data ref.current.push({ id: "sensor-1", x: 42, y: 87 }) // Remove by ID when the reading expires ref.current.remove("sensor-1") // Batch add ref.current.pushMany(latestReadings)
Update: Bar Chart
When to use: Dashboards where values change but categories stay the same. A KPI dashboard where revenue updates every minute. A leaderboard where scores change. update() modifies the datum in place — the bar resizes without removing and re-adding it, preserving buffer position and (when transitions are enabled) animating smoothly.
Click "Update Random Bar" to change one bar's value. "Randomize All" updates every bar. "Add Region" pushes a new category. "Remove Random" deletes one.
JSX
const ref = useRef() <BarChart ref={ref} categoryAccessor="category" valueAccessor="value" dataIdAccessor="id" // required for remove/update on ordinal charts colorBy="category" /> // Initialize ref.current.push({ id: "north", category: "North", value: 42 }) // Update a value — bar resizes, no remove+push needed ref.current.update("north", d => ({ ...d, value: 58 })) // Add a new category ref.current.push({ id: "central", category: "Central", value: 33 }) // Remove a category entirely ref.current.remove("north")
Network: Remove Node with Edge Cascade
When to use: Infrastructure monitoring where services go offline. Team network analysis where a member leaves. Dependency graphs where a package is removed. When you remove a node from a network, all edges connected to it are automatically removed — the graph stays consistent.
Click "Remove Cache" to delete the Cache node. Both edges connected to it (API→Cache and Cache→DB) disappear automatically. "Add Web→DB Edge" pushes a new connection. "Rebuild" restores the original topology.
JSX
// Network HOCs expose remove/update for nodes via the standard ref API const ref = useRef() // Remove a node by ID — connected edges cascade automatically ref.current.remove("Cache") // Update a node's data (e.g., change its status or weight) ref.current.update("API", d => ({ ...d, status: "degraded" })) // For edge-level operations (removeEdge, updateEdge), use StreamNetworkFrame: // const frameRef = useRef<StreamNetworkFrameHandle>() // frameRef.current.removeEdge("Web", "API") // frameRef.current.updateEdge("Web", "API", d => ({ ...d, value: 200 }))
When to use push vs. controlled data
The push API and the data prop are two ways to feed data into a chart. Use the right one for your scenario:
Push API
- Data arrives over time (WebSocket, SSE, polling)
- Individual items added, removed, or updated
- High-frequency updates (100+ per second)
- Chart manages its own buffer (RingBuffer)
- Omit the
data prop entirely
Controlled data prop
- Data loaded from an API or file
- Entire dataset replaced at once
- Data managed by React state or a store
- Standard React data flow
- Pass
data={myData} prop
Bridge: a controlled array that drives the push buffer
Often you hold rows in React state — a filtered window, a replay cursor, an aggregated slice — but still want the chart to ingest themincrementally rather than re-initialize on every render.useSyncedPushData is that reconciliation, done once: it diffs your array by id and issues the minimal push/update/remove calls, clearing and rebuilding when a resetKey changes (a theme remount, a mode switch). Pass the rows to the hook, not to the chart’s data prop.
JSX
import { useSyncedPushData } from "semiotic" const ref = useRef() const [rows, setRows] = useState([]) // your controlled window useSyncedPushData(ref, rows, { id: "id" }) // mirror rows → push buffer // clear + rebuild when something invalidates the whole series: useSyncedPushData(ref, rows, { id: "id", resetKey: theme }) <RealtimeLineChart ref={ref} timeAccessor="t" valueAccessor="v" pointIdAccessor="id" /> // no data prop — the hook owns ingestion
It works on any push-capable HOC (realtime, ordinal, XY, network) and pairs with useStreamStatus for a live/stale badge. The realtime examples — Wikipedia, The Long Way Around, and The Scroll You’re Telling — all drive their charts through it.
ID accessors by chart type
JSX
// XY charts: pointIdAccessor <Scatterplot pointIdAccessor="id" /> <LineChart pointIdAccessor="id" /> // Ordinal charts: dataIdAccessor <BarChart dataIdAccessor="id" /> <PieChart dataIdAccessor="id" /> // Network charts: nodeIDAccessor (already required) <ForceDirectedGraph nodeIDAccessor="id" /> // remove/update use node IDs directly — no extra accessor needed // Geo charts: pointIdAccessor <ProportionalSymbolMap pointIdAccessor="id" />
Important notes
- Omit
data when using push. Passing data={[]} clears pushed data on every render. Just don't pass the prop. - ID accessors are required for remove/update. Without them, the store can't find the item.
push() and pushMany() work without IDs. - Network edge cascade. Removing a node automatically removes all connected edges. You don't need to remove edges manually.
- Extent tracking. The store tracks min/max values incrementally. When you remove an item that was the min or max, the extent is marked dirty and recalculated on the next scene computation.
- Update preserves buffer position. Unlike remove+push,
update() modifies the datum in place. This matters for decay encoding (age-based opacity) — an updated item keeps its original age.