Skip to content

Next.js

WriteTrack uses browser APIs and must run client-side only. Here’s how to integrate it with Next.js.

Add the 'use client' directive to components using WriteTrack:

'use client';
import { useRef, useEffect } from 'react';
import { WriteTrack } from 'writetrack';
export function ResponseForm() {
const textareaRef = useRef<HTMLTextAreaElement>(null);
const trackerRef = useRef<WriteTrack | null>(null);
useEffect(() => {
if (textareaRef.current) {
trackerRef.current = new WriteTrack({ target: textareaRef.current });
trackerRef.current.start();
}
return () => {
trackerRef.current?.stop();
};
}, []);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (trackerRef.current) {
const data = trackerRef.current.getData();
const events = trackerRef.current.getRawEvents();
console.log(`Captured ${events.length} events`, data.quality);
}
};
return (
<form onSubmit={handleSubmit}>
<textarea ref={textareaRef} rows={10} />
<button type="submit">Submit</button>
</form>
);
}

The writetrack/react hook handles client-side concerns automatically:

'use client';
import { useRef } from 'react';
import { useWriteTrack } from 'writetrack/react';
export function ResponseForm() {
const textareaRef = useRef<HTMLTextAreaElement>(null);
const { isTracking, tracker } = useWriteTrack(textareaRef);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (tracker) {
const data = tracker.getData();
console.log('Session:', data.quality);
}
};
return (
<form onSubmit={handleSubmit}>
<textarea ref={textareaRef} rows={10} />
<button type="submit">Submit</button>
</form>
);
}

For the Pages Router, either use the 'use client' approach above, or use dynamic imports:

import dynamic from 'next/dynamic';
const ResponseForm = dynamic(() => import('../components/ResponseForm'), {
ssr: false,
});
export default function Page() {
return <ResponseForm />;
}

Send captured data to Server Actions:

'use client';
import { useRef } from 'react';
import { useWriteTrack } from 'writetrack/react';
import { submitResponse } from './actions';
export function ResponseForm() {
const textareaRef = useRef<HTMLTextAreaElement>(null);
const { tracker } = useWriteTrack(textareaRef);
async function handleSubmit(formData: FormData) {
if (tracker) {
formData.set('keystrokeCount', String(tracker.getKeystrokeCount()));
}
await submitResponse(formData);
}
return (
<form action={handleSubmit}>
<textarea ref={textareaRef} name="content" rows={10} />
<button type="submit">Submit</button>
</form>
);
}
actions.ts
'use server';
export async function submitResponse(formData: FormData) {
const content = formData.get('content') as string;
const keystrokeCount = formData.get('keystrokeCount') as string;
// Store or process the submission
await db.responses.create({
content,
keystrokeCount: Number(keystrokeCount),
});
}

Alternatively, send data to API routes:

const handleSubmit = async () => {
if (!tracker) return;
const data = tracker.getData();
await fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
content: textareaRef.current?.value,
typingData: data,
}),
});
};