Headless Forecasting, Custom UI Live Demo
DatastripesForecast is the fastest way to ship a forecast, but every piece of it is built on public APIs you can use directly when you want your own UI:
useWasmForecast, a React hook (@datastripes/forecast/react) and a Vue composable (@datastripes/forecast/vue) that run the engine and hand you reactiveresult,isLoading,error, andalgorithmstate, plus anexecute()function.executeForecast(), the plain async function from the root@datastripes/forecastentry, with no UI framework involved at all.
Live demo: a custom widget on useWasmForecast
Everything below (the algorithm picker, the metric tiles, the five-day table) is ordinary page markup. The only Datastripes code is the useWasmForecast call from @datastripes/forecast/vue.
Changing the algorithm re-runs the forecast automatically: algorithm is a reactive ref returned by the composable, and the composable re-executes when it changes. Recalculate calls execute() explicitly.
Source (Vue)
vue
<script setup>
import { computed } from 'vue';
import { useWasmForecast } from '@datastripes/forecast/vue';
const { result, isLoading, algorithm, execute } = useWasmForecast({
data: saasData,
horizon: 21,
algorithm: 'holt_winters_add',
columnMapping: { timeKey: 'date', targetKey: 'annual_recurring_revenue' }
});
const nextFive = computed(() =>
(result.value?.points ?? []).filter((p) => p.forecast != null && p.actual == null).slice(0, 5)
);
</script>
<template>
<select v-model="algorithm">
<option value="holt_winters_add">Holt-Winters (additive)</option>
<option value="prophet_lite">Prophet-lite</option>
<option value="arima">ARIMA</option>
</select>
<button @click="execute()">Recalculate</button>
<p v-if="isLoading && !result">Running the forecast…</p>
<ul v-else-if="result">
<li>R²: {{ (result.metrics.r2 * 100).toFixed(1) }}%</li>
<li v-for="p in nextFive" :key="p.index">{{ p.dateStr }}: {{ Math.round(p.forecast) }}</li>
</ul>
</template>Source (React)
The React hook has the same shape, with setter functions instead of refs:
tsx
import { useWasmForecast } from '@datastripes/forecast/react';
export function ArrForecastWidget({ data }) {
const { result, isLoading, algorithm, setAlgorithm, execute } = useWasmForecast({
data,
horizon: 21,
algorithm: 'holt_winters_add',
columnMapping: { timeKey: 'date', targetKey: 'annual_recurring_revenue' }
});
if (isLoading && !result) return <p>Running the forecast…</p>;
if (!result) return null;
return (
<div>
<select value={algorithm} onChange={(e) => setAlgorithm(e.target.value as any)}>
<option value="holt_winters_add">Holt-Winters (additive)</option>
<option value="prophet_lite">Prophet-lite</option>
<option value="arima">ARIMA</option>
</select>
<button onClick={() => execute()}>Recalculate</button>
<p>R²: {(result.metrics.r2 * 100).toFixed(1)}%</p>
</div>
);
}No UI at all: executeForecast()
When you don't need any component state, for example to feed your own chart library or to run a forecast inside a Web Worker, call the engine directly from the root entry:
ts
import { executeForecast } from '@datastripes/forecast';
const result = await executeForecast({
data: myRecords, // records, a CSV string, or a JSON array
algorithm: 'prophet_lite',
horizon: 30
});
const forecastPoints = result.points.filter((p) => p.forecast != null && p.actual == null);
console.log('R²:', result.metrics.r2);
console.log('Execution time:', result.metrics.executionTimeMs, 'ms');The What-if scenarios demo is built this way: it calls executeForecast() on every scenario change and passes the result to the standalone DatastripesKpiDeck and DatastripesForecastChart components. See Types for the full ForecastOutput shape.