| ⚡ Built for throughput | TypedArray (Float64Array) candle buffers, O(1) append/update, RAF-coalesced tick merging, and a three-layer canvas split so a moving crosshair never repaints the chart. |
| 🧩 Framework-agnostic core | All rendering, data and interaction logic lives in @rekurt/openkline-core with zero runtime dependencies. React and Vue are thin, idiomatic wrappers — not reimplementations. |
| 🎯 Full API parity | The same declarative props and imperative methods across vanilla, React and Vue. Indicators are plain config objects; chart state round-trips through saveLayoutState / loadState. |
| ⌨️ Keyboard-first & accessible | Pan, zoom and navigate without a mouse. Honors prefers-reduced-motion and prefers-color-scheme. |
| 📈 Batteries included | 25+ indicators, 10 drawing tools, alerts, compare mode, volume profile, Heikin-Ashi & Renko transforms, SVG/PNG export, and an 'auto' dark/light theme. |
| 🔒 Strict & tested | Strict TypeScript (incl. noUncheckedIndexedAccess), 0 lint warnings in CI, and 440+ unit tests covering rendering math, indicators and state. |
| Package | Repository | What it is |
|---|---|---|
@rekurt/openkline-core |
this repo | Framework-agnostic rendering + data + interaction (no React/Vue). |
@rekurt/openkline-react |
rekurt/openkline-react | React 18+/19 wrapper — <OHLCVChart> component + useOHLCVChart hook. |
@rekurt/openkline-vue |
rekurt/openkline-vue | Vue 3 wrapper — <OHLCVChart> component + useOHLCVChart composable. |
The React and Vue wrappers were extracted from this monorepo into their own repositories — each ships its own demo app, tests and CI.
# Vanilla TypeScript / bring-your-own framework
npm install @rekurt/openkline-core
# React
npm install @rekurt/openkline-core @rekurt/openkline-react
# Vue 3
npm install @rekurt/openkline-core @rekurt/openkline-vue
Pre-release note: until the packages are published to npm, the wrapper repos vendor a built core tarball so
npm installworks out of the box. See each wrapper’s README fornpm run update:core.
import { OHLCVChart } from '@rekurt/openkline-core';
const chart = new OHLCVChart({
container: document.getElementById('chart')!,
symbol: 'BTC/USDT',
resolution: '1H',
theme: 'auto',
onError: (err) => console.error('[openkline]', err),
});
chart.setData(historicalCandles);
// Declarative indicators via config objects — the same path the React and
// Vue wrappers use. `saveLayoutState` round-trips these configs.
chart.setIndicatorConfigs([
{ type: 'sma', period: 20 },
{ type: 'ema', period: 50 },
{ type: 'bb', period: 20, stdDev: 2 },
]);
// Live mode
setInterval(() => chart.updateLastCandle(latestCandle), 500);
// Shareable chart state — save to a query param, load from one
const share = btoa(JSON.stringify(chart.saveLayoutState()));
chart.loadState(JSON.parse(atob(share)));
Lives in rekurt/openkline-react.
import { useRef, useMemo } from 'react';
import { OHLCVChart, type OHLCVChartRef } from '@rekurt/openkline-react';
import type { Candle, IndicatorConfig } from '@rekurt/openkline-core';
export function App({ candles }: { candles: Candle[] }) {
const chartRef = useRef<OHLCVChartRef>(null);
// Indicators are a plain config array — no `new SMA(20)` in user code.
const indicators = useMemo<IndicatorConfig[]>(
() => [
{ type: 'sma', period: 20 },
{ type: 'ema', period: 50 },
{ type: 'bb', period: 20, stdDev: 2 },
],
[],
);
return (
<div style=>
<OHLCVChart
ref={chartRef}
symbol="BTC/USDT"
resolution="1H"
data={candles}
theme="auto"
chartType="candles"
indicators={indicators}
onHover={(info) => console.log('hovered', info?.index)}
onError={(err) => console.error('[openkline]', err)}
/>
<button onClick={() => chartRef.current?.goToLive()}>Go live</button>
</div>
);
}
Lives in rekurt/openkline-vue.
<script setup lang="ts">
import { ref } from 'vue';
import { OHLCVChart } from '@rekurt/openkline-vue';
import type { Candle, IndicatorConfig } from '@rekurt/openkline-core';
defineProps<{ candles: Candle[] }>();
const chartRef = ref<InstanceType<typeof OHLCVChart>>();
const indicators = ref<IndicatorConfig[]>([
{ type: 'sma', period: 20 },
{ type: 'rsi', period: 14 },
]);
</script>
<template>
<div style="width: 100%; height: 600px">
<OHLCVChart
ref="chartRef"
symbol="BTC/USDT"
resolution="1H"
:data="candles"
theme="auto"
chart-type="candles"
v-model:indicators="indicators"
@hover="(info) => console.log('hovered', info?.index)"
@error="(err) => console.error('[openkline]', err)"
/>
<button @click="chartRef?.goToLive()">Go live</button>
</div>
</template>
openkline (this monorepo)
└─ packages/core @rekurt/openkline-core framework-agnostic engine (zero deps)
├─ rendering ChartEngine, layers, axes, legend, WebGL candle renderer
├─ data CandleBuffer, CandleMerger, DataFeed, transports
├─ interaction Viewport, KeyboardController, autoFollow state machine
├─ indicators 25+ indicators + registry + createIndicator factory
├─ drawings buffer-anchored drawing tools + DrawingLayer
├─ transforms Heikin-Ashi, Renko
└─ export toSVG, PNG
rekurt/openkline-react @rekurt/openkline-react thin React wrapper
rekurt/openkline-vue @rekurt/openkline-vue thin Vue 3 wrapper
The wrappers own only framework glue (lifecycle, refs, reactivity). Every pixel and every number comes from the core, which is why parity is “for free.”
npm install
npm run dev:playground # unified demo → http://localhost:5176
npm run dev:core # vanilla demo → http://localhost:5173
npm run lint # ESLint, --max-warnings 0
npm run typecheck # strict tsc across all tsconfigs
npm test # vitest — 440+ tests
npm run build # tsup bundle for @rekurt/openkline-core
npm run docs # TypeDoc → docs/api/
For the React and Vue wrapper demos, see rekurt/openkline-react and rekurt/openkline-vue.
See CONTRIBUTING.md for branch, commit and review conventions, and CLAUDE.md for how AI agents should work here.
noUncheckedIndexedAccess.--max-warnings 0).0.1.0 is the first public release: the core primitives are stable and
well-tested. Upcoming milestones add deeper multi-pane integration, more
indicators and drawing tools, alerts, replay mode, compare mode, workspaces and
internationalization. See the CHANGELOG and the
M1 design doc.
MIT © OpenKline contributors