Skip to content

React Integration

WriteTrack provides a React hook for plain <textarea>, <input>, and contenteditable elements. If you’re using a rich text editor (TipTap, CKEditor, Lexical, etc.), use the editor-specific extension instead — it handles lifecycle management through the editor’s plugin system.

Terminal window
npm i writetrack

React is an optional peer dependency - non-React users won’t have it in their bundle.

ResponseForm.tsx
import React, { useRef } from 'react';
import { useWriteTrack } from 'writetrack/react';
export function ResponseForm() {
const textareaRef = useRef<HTMLTextAreaElement>(null);
const { tracker, isTracking } = useWriteTrack(textareaRef);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (tracker) {
const data = tracker.getData();
console.log('Session data:', data);
}
};
return (
<form onSubmit={handleSubmit}>
<textarea ref={textareaRef} placeholder="Type your response..." />
{isTracking && <p>Tracking keystrokes...</p>}
<button type="submit">Submit</button>
</form>
);
}
function useWriteTrack(
ref: RefObject<HTMLElement | null>,
options?: UseWriteTrackOptions
): UseWriteTrackReturn;
ParameterTypeDescription
refRefObject<HTMLElement>React ref attached to the input element
options.licensestringWriteTrack license key
options.userIdstringUser identifier included in metadata
options.contentIdstringContent identifier included in metadata
options.metadataRecord<string, unknown>Additional metadata
options.autoStartbooleanAuto-start tracking when element mounts (default: true)
options.wasmUrlstringURL to WASM binary for analysis
options.persistbooleanEnable IndexedDB session persistence (requires contentId)
options.analysisbooleanSubscribe to real-time analysis events (requires WASM)
options.onTick(data: { activeTime: number; totalTime: number; tracker: WriteTrack }) => voidCallback fired every ~1s with active session time and tracker instance
options.onReady(tracker: WriteTrack) => voidCallback fired when tracker is ready

Callbacks (onTick, onReady) are always up to date — no need for useCallback or manual refs.

PropertyTypeDescription
start() => voidStart tracking (if autoStart was false)
stop() => voidStop tracking
reset() => voidReset tracker and clear state
isTrackingbooleanWhether tracker is active
trackerWriteTrack | nullUnderlying WriteTrack instance
isReadybooleanWhether tracker has initialised
analysisSessionAnalysis | nullLatest analysis result (when analysis: true)
errorError | nullLatest error from WASM or initialisation
import { useRef } from 'react';
import { useWriteTrack } from 'writetrack/react';
function AnalyzedForm() {
const textareaRef = useRef<HTMLTextAreaElement>(null);
const { analysis, isReady, error } = useWriteTrack(textareaRef, {
analysis: true,
});
return (
<div>
<textarea ref={textareaRef} />
{!isReady && <p>Initialising...</p>}
{error && <p>Error: {error.message}</p>}
{analysis && <p>Origin: {analysis.contentOrigin.indicator.code}</p>}
</div>
);
}
ControlledForm.tsx
import React, { useRef } from 'react';
import { useWriteTrack } from 'writetrack/react';
export function ControlledForm() {
const inputRef = useRef<HTMLInputElement>(null);
const { start, stop, tracker, isTracking } = useWriteTrack(inputRef, {
autoStart: false,
});
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={isTracking ? stop : start}>
{isTracking ? 'Stop' : 'Start'} Tracking
</button>
<button onClick={() => tracker && console.log(tracker.getData())}>
Get Data
</button>
</div>
);
}

Call useWriteTrack once per field you want to track. Each hook manages its own independent tracker instance.

function MultiFieldForm() {
const titleRef = useRef<HTMLInputElement>(null);
const bodyRef = useRef<HTMLTextAreaElement>(null);
const title = useWriteTrack(titleRef, { contentId: 'title' });
const body = useWriteTrack(bodyRef, { contentId: 'body' });
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const fields = [
{ name: 'title', data: title.tracker?.getData() },
{ name: 'body', data: body.tracker?.getData() },
];
console.log('Session data:', fields);
};
return (
<form onSubmit={handleSubmit}>
<input ref={titleRef} placeholder="Title (tracked)" />
<textarea ref={bodyRef} placeholder="Body (tracked)" />
<button type="submit">Send</button>
</form>
);
}
import type {
UseWriteTrackReturn,
UseWriteTrackOptions,
} from 'writetrack/react';