use-chrome-ai
Core: GitHub · use-chrome-ai
React: React Demo · @use-chrome-ai/react
Vue: Vue Demo · @use-chrome-ai/vue
After playing with Chrome's built-in AI APIs, I wanted a small wrapper that handled the repetitive bits: availability, download progress, streaming, aborts, and session lifecycle and integrated into react easily.
The package shape
The core package is framework-agnostic. The React package exports hooks, and the Vue package exports composables plus the same core factories. None of them ship UI, they just give you a stable state model around the browser APIs.
npm i use-chrome-ai
npm i @use-chrome-ai/react
npm i @use-chrome-ai/vueOne-hook chat
The useChat hook allows you access to prompt API and can set up a basic chat bot in one hook.
import { useChat } from "@use-chrome-ai/react";
export function Chat() {
const { messages, input, setInput, send, stop, isStreaming, model } = useChat(
{
system: "You are a helpful assistant.",
},
);
if (model.isUnavailable) return <p>Built-in AI is not available here.</p>;
if (model.availability === "downloadable") {
return (
<button onClick={() => model.download()}>Enable on-device AI</button>
);
}
if (model.isDownloading) return <progress value={model.progress} max={1} />;
return (
<form
onSubmit={(event) => {
event.preventDefault();
void send();
}}
>
{messages.map((message, index) => (
<p key={index}>
<b>{message.role}:</b> {message.content}
</p>
))}
<input value={input} onChange={(event) => setInput(event.target.value)} />
<button disabled={isStreaming}>Send</button>
{isStreaming && <button onClick={stop}>Stop</button>}
</form>
);
}Using the other APIs
The React bindings follow the same shape across the task APIs: call the hook, read model, run the task, and render result or error. The names map directly to Chrome's APIs, which keeps the wrapper boring in a good way.
import {
useLanguageDetector,
usePrompt,
useProofreader,
useRewriter,
useSummarizer,
useTranslator,
useWriter,
} from "@use-chrome-ai/react";
function AiTools({ article, draft }: { article: string; draft: string }) {
const promptApi = usePrompt();
const summarizer = useSummarizer({ type: "key-points", length: "short" });
const writer = useWriter({ tone: "formal", length: "short" });
const rewriter = useRewriter({ tone: "more-casual" });
const proofreader = useProofreader();
const translator = useTranslator({
sourceLanguage: "en",
targetLanguage: "es",
});
const detector = useLanguageDetector();
async function runExamples() {
await promptApi.prompt("Give me one label for this page.");
await summarizer.summarize(article);
await writer.write("Ask a customer for a missing invoice number.");
await rewriter.rewrite(draft);
await proofreader.proofread("I seen the bug yesturday.");
await translator.translate("Good morning");
await detector.detect("bonjour le monde");
}
return <button onClick={runExamples}>Run AI helpers</button>;
}Vue and core
The Vue adapter has useChat and useModelStatus, and it re-exports the core controllers. That means Vue can use the same lower-level createSummarizer, createWriter, createRewriter, createTranslator, createProofreader, and createLanguageDetector APIs without waiting for a dedicated composable per feature.
import { createSummarizer, useModelStatus } from "@use-chrome-ai/vue";
const summarizer = createSummarizer({ type: "key-points", length: "short" });
const status = useModelStatus(summarizer);
async function summarize(article: string) {
if (status.value.availability === "downloadable") {
await summarizer.download();
}
return summarizer.run({ text: article });
}