Provider Fallback
React hook for per-capability Chrome Built-in AI ⇄ Transformers.js provider resolution with truthful badge provenance.
Provider Fallback Hook
See it in action
Try the Writing Tools blocks for a working demo — the Write / Translate / Summarize / Complete blocks resolve Chrome Built-in AI independently per capability and fall back to Transformers.js behind an explicit download gate, rendering a truthful provider badge derived from this hook.
useProviderFallback
Resolve each capability (Summarizer, Translator, AI-edit, fill-mask) to Chrome Built-in AI or Transformers.js. Detection is independent per capability and reads the browser globals directly, so one capability can be Chrome-AI-served while another falls back in the same session. Every provider package is loaded via a dynamic import() inside the resolution path, so @localmode/react gains no hard dependency on @localmode/chrome-ai or @localmode/transformers — nothing about a provider ships until a capability first resolves. Resolution is lazy and session-cached, and each result carries truthful resolution provenance for a badge.
import { useProviderFallback, providerName } from '@localmode/react';
function SummarizeTab() {
const { resolveSummarizer, resolution, isResolving } = useProviderFallback();
async function run(text: string) {
const { model } = await resolveSummarizer({
chromeStyle: 'key-points',
length: 'medium',
fallbackModelId: 'Xenova/distilbart-cnn-6-6',
});
return summarize({ model, text });
}
return resolution ? <Badge provider={providerName(resolution.provider)} tier={resolution.tier} /> : null;
}Options
Prop
Type
In the browser, inject static loader overrides
The default loadChromeAI / loadTransformers loaders use a Function-constructed import() so @localmode/react keeps no hard dependency on the provider packages. That indirection is opaque to bundlers, so a browser build can't resolve the bare specifier at runtime. When you consume this hook in the browser, pass loaders with literal imports the bundler can see:
useProviderFallback({
loadChromeAI: () => import('@localmode/chrome-ai'),
loadTransformers: () => import('@localmode/transformers'),
});Return Value
Prop
Type
Resolver Params
Each resolver takes exactly the params its capability needs, so the split Write / Translate / Summarize / Complete surfaces each import only one. resolveFillMask takes a plain modelId: string (no Chrome equivalent).
ResolveSummarizerParams
Prop
Type
ResolveTranslatorParams
Prop
Type
ResolveEditEngineParams
Prop
Type
ResolvedModel and provenance
Every resolver returns a ResolvedModel<M> — the concrete model instance plus the provenance a badge derives from — and updates the hook's resolution, so a badge reflects what actually serves requests, never the detection probe alone.
Prop
Type
Standalone detectors and badge helpers
The detection functions are exported standalone (all async, all returning a ProviderId of 'chrome-ai' | 'transformers'). They read the same browser surfaces the @localmode/chrome-ai detectors read, so no provider import is needed to detect.
| Function | Signature | Behavior |
|---|---|---|
detectSummarizerProvider | () => Promise<ProviderId> | 'chrome-ai' when the Chrome Summarizer API is present, else 'transformers'. |
detectTranslatorProvider | () => Promise<ProviderId> | 'chrome-ai' when the Chrome Translator API is present, else 'transformers'. |
detectPromptProvider | () => Promise<ProviderId> | Gated on LanguageModel.availability() === 'available' — presence alone is not enough, which prevents committing to a merely-downloadable Gemini Nano. Every other state falls back to 'transformers'. The legacy self.ai.languageModel surface (no availability()) trusts presence. |
providerTier | (provider: ProviderId) => ProviderTier | Maps 'chrome-ai' → 'built-in', 'transformers' → 'download'. |
providerName | (provider: ProviderId) => string | Human-readable name: 'Chrome AI' or 'Transformers.js'. |
import { detectPromptProvider, providerName, providerTier } from '@localmode/react';
const provider = await detectPromptProvider(); // 'chrome-ai' only when Gemini Nano is actually 'available'
const label = providerName(provider); // 'Chrome AI' | 'Transformers.js'
const tier = providerTier(provider); // 'built-in' | 'download'For the Chrome Built-in AI provider (Summarizer, Translator, Prompt APIs) see the Chrome AI guide; for the Transformers.js fallback models see the Transformers provider guide.
Blocks
| App | Description | Links |
|---|---|---|
| Writing Tools | Per-capability Chrome AI ⇄ Transformers.js resolution with a truthful provider-fallback badge via useProviderFallback | Live block · Source |