Embedding graphs
Put a graph you built in the dashboard into your web or mobile app.
Every embed needs the same two things: the graph id from Dashboard → Graphs (select Embed on a card to copy it) and the Terra user id of the person looking at it.
Use example in place of a user id to render generated data while you build.
Web
On the web, graphs render directly into your page. There's no iframe, so the chart sizes to its container, inherits your layout, and behaves like any other element.
npm install @tryterra/graphs-reactimport { TerraGraph } from "@tryterra/graphs-react";
export function StepsCard({ terraUserId }) {
return (
<TerraGraph
sessionId="YOUR_GRAPH_ID"
userId={terraUserId}
timeframe={30}
style={{ width: "100%", height: 360 }}
onError={(error) => console.error(error.message, error.traceId)}
/>
);
}Works in Next.js, Remix and Astro with no dynamic() wrapper — the component renders nothing on the server and mounts in the browser.
npm install @tryterra/graphs<script type="module">
import "@tryterra/graphs";
</script>
<terra-graph
session-id="YOUR_GRAPH_ID"
user-id="TERRA_USER_ID"
timeframe="30"
></terra-graph>
<style>
terra-graph { display: block; width: 100%; height: 360px; }
</style>Tell the Vue compiler that terra-graph is a custom element, in vite.config.js:
The element has no height of its own — give it one, or it collapses.
React Native
React Native has a package of its own, which draws the graph as native views — no web view:
There is no chart engine to install or configure — the package ships its own. The one thing it can't bundle is a drawing surface, since React Native has none built in: react-native-svg is a native module, declared as a peer so npm installs it for you.
On Expo, run npx expo install react-native-svg once afterwards so the version matches your SDK.
For faster painting on very dense charts, import @tryterra/graphs-react-native/skia instead and add @shopify/react-native-skia and react-native-reanimated. Same component, same props.
One difference worth knowing. On the web, the chart engine is loaded from Terra at render time, so improvements reach your users as soon as we ship them. A native app bundles it instead, so it picks them up when you upgrade the package and release a new build. If you would rather never think about that, the web view below is the option that keeps it.
Other mobile platforms
iOS, Android and Flutter load the hosted graph in a web view. React Native can too, with react-native-webview, if you would rather not add a charting library:
The same URL works in an <iframe> on the web, if you'd rather not add a package.
Choosing the date range
Every embed takes the same three settings — as props in React, attributes in HTML, and query parameters in a URL.
timeframe = 30
The most recent 30 days, including today.
from = 2026-08-01, to = 2026-08-31
1–31 August, with to included.
timeframe = 7, from = 2026-08-01
Seven days starting 1 August.
timeframe = 7, to = 2026-08-31
The seven days ending 31 August.
from = 2026-08-01
1 August through today.
Dates are YYYY-MM-DD in UTC. A range can cover at most 92 days. With nothing set, you get the last 7 days.
Pass a date string, not a JavaScript Date. A graph window is a run of calendar days, and a Date is a moment in time — converting one to the other picks a timezone, and the usual way of doing it picks the wrong one. A date picker gives you new Date(2026, 7, 1) for "1 August", and .toISOString().slice(0, 10) turns that into 2026-07-31 for anyone east of Greenwich.
The packages type these as IsoDate, so a Date won't compile. If you have one, convert it with the helper they export:
Changing the range redraws the chart in place rather than reloading it, so you can wire a date picker straight to it:
A graph set to Single session scope ignores the range and always shows the user's latest workout, night or day.
Matching your app's theme
A graph's colours are part of its design in the dashboard, so the usual answer is to set them there — every embed picks the change up without a release on your side.
For colours that have to follow the app at runtime, such as a dark-mode toggle, pass a theme:
It's a theme property on the element too, if you aren't using React:
Give colours as hex or rgb(). If you need two colour schemes of the same chart, building two graphs in the dashboard is usually simpler than theming at runtime.
When a graph doesn't render
A graph that can't be drawn says so in place, and reports a trace id. Quote that id to Terra support and we can see exactly what happened.
On the element it's a terra-graph:error event carrying the same thing, plus a data-state attribute (loading, ready or error) and data-trace-id on failure.
The common causes:
No data found for this time period
Terra holds no data for that user over that range. Check the user in Dashboard → Users, and widen the range.
Invalid graph session ID
The graph id is wrong, or the graph was deleted. Copy it again from Embed.
Invalid graph type requested
The graph was created against an old graph type. Open it in the editor and re-pick the metric.
Nothing renders, no error
The element has no height. Give it one in CSS.
A blocked request in the console
A Content Security Policy is blocking api.tryterra.co — see the note above.
Changing a graph after you've shipped
Graph ids are stable. Edit a graph in the dashboard — different metric, different colours, an extra header stat — and every embed of it changes on the next load. You don't need to touch your app.
Deleting a graph stops it rendering everywhere it's embedded, so check Viewers first if you're not sure who's using it.
Last updated
Was this helpful?