WordInk GitHub

Quickstart: React

@wordink/react needs React 19. It brings @wordink/web and @wordink/core with it.

npm install @wordink/react

The ready-made button: <WordInkMic>

import { WordInkMic } from "@wordink/react";

export function Message() {
  return (
    <>
      <textarea id="message" />
      <WordInkMic htmlFor="message" endpoint="/api/wordink" shortcut="Alt+D" onError={(e) => console.warn(e.hint)} />
    </>
  );
}

<WordInkMic> is the <wordink-mic> element as a typed component: the default button, keyboard shortcut, level meter and status, with text inserted at the cursor of the bound field. For a controlled field the insertion fires an input event, so your onChange sees it. Props are the element's attributes and properties (htmlFor, mode, provider, endpoint, shortcut, devKey, hint, model, transform, transformTimeoutMs) plus onStart, onInterim, onTranscript and onError.

endpoint is your @wordink/server relay.

Your own UI: useDictation

import { useDictation } from "@wordink/react";
import { useState } from "react";

export function Message() {
  const [text, setText] = useState("");
  const { state, level, interim, error, press, release } = useDictation({
    provider: "groq",
    endpoint: "/api/wordink",
    onTranscript: (t) => setText((prev) => (prev ? `${prev} ${t}` : t)),
  });

  return (
    <>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <button onPointerDown={() => press()} onPointerUp={() => release()}>
        {state === "listening" ? "Listening…" : "Hold to talk"}
      </button>
      <meter min={0} max={1} value={level} />
      <p role="status">{error ? `${error.message} ${error.hint}` : interim}</p>
    </>
  );
}

The dictation is created on the first press, recreated when an option other than the callbacks changes, and destroyed on unmount, which releases the microphone. Call press() / start() from a user gesture. Both exports are safe to import during server rendering.

With the local engine

npm install @wordink/local
import { useDictation } from "@wordink/react";
import { createLocalProvider } from "@wordink/local";

const local = createLocalProvider(); // create it once, outside render

export function Message() {
  const dictation = useDictation({ provider: local, onTranscript: (t) => console.log(t) });
  // …
}

Create the provider once (module scope or useMemo): a new provider object counts as a changed option and recreates the dictation. With Vite, keep the package out of dependency pre-bundling so its worker loads: optimizeDeps: { exclude: ["@wordink/local"] }.

The full option and result tables are in the @wordink/react README.