useLingo() is how components get to the runtime. It reads the nearest <LingoProvider> and returns the active Lingo object — call it once per component, hold the reference, and use whichever methods you need.
import { useLingo } from "@lingo.dev/react";
function Greeting() {
const l = useLingo();
return <p>{l.text("Hello", { context: "Hero heading" })}</p>;
}It throws if called outside a provider, so test setups need to wrap the render — even with messages: {} if you don't care about translations in the test.
l.text(source, options) — plain strings#
The everyday call. Returns a translated string, or the source if no translation exists for the current locale.
l.text("Save", { context: "Form button" });
// → "Speichern" (de) / "Save" (en, fallback)Interpolation#
{placeholder} segments are substituted from options.values.
l.text("Welcome back, {name}!", {
values: { name: user.firstName },
context: "Dashboard greeting",
});Missing values render as {name} literally — handy for spotting bugs during dev, but make sure your tests assert on the rendered result so empty values don't slip through.
When the source contains ICU syntax#
If the source string contains plural / select / number / date markers, the runtime upgrades automatically — no extra API. See Plurals and select for the friendly wrapper.
l.rich(source, options) — React subtrees#
When the translated text contains React components (links, bold, an <Icon/>), use l.rich. The translation string carries placeholder tags like <link>...</link>; you map each tag to a renderer.
l.rich("Click <link>here</link> for {topic}", {
tags: {
link: (children) => <a href="/help">{children}</a>,
},
values: { topic: "details" },
context: "Footer help link",
});
// → <>Click <a href="/help">here</a> for details</>Self-closing tags work too:
l.rich("Loading <spinner/>...", {
tags: { spinner: () => <Spinner /> },
context: "Inline loading state",
});Tags without a renderer fall back to the raw text — so the missing-tag case is visible in dev, not silently swallowed.
Don't put markup directly inside translated strings. l.rich exists so translators see neutral placeholders (<link>) instead of <a href="...">, which they'd have to preserve verbatim and often break. Define the renderer in your code, not in the locale file.
Locale metadata on l#
Beyond translation, the object exposes:
| Property | Type | Notes |
|---|---|---|
l.locale | string | Whatever you passed to LingoProvider. BCP-47. |
l.direction | "ltr" | "rtl" | Computed via Intl.Locale.textInfo + fallback RTL language list. |
l.script | string | undefined | Inferred when possible ("Latn", "Cyrl", "Arab", …). |
l.region | string | undefined | Inferred from BCP-47 ("US", "DE", "SA", …). |
Useful for layout decisions:
const l = useLingo();
return <div dir={l.direction}>...</div>;Formatting methods#
l also carries num, currency, percent, date, time, datetime, relative, list, displayName, sort, segment, fileSize, compact, unit — see Formatting for the full breakdown. These are thin wrappers around native Intl.* formatters keyed to l.locale.
Outside of React#
useLingo only works inside components. For utilities, route loaders, or server code, build the same object directly:
import { createLingo } from "@lingo.dev/react";
const l = createLingo("es", messages);
l.text("Hello", { context: "Email subject" });It's the same Lingo shape, no provider needed. LingoProvider itself uses createLingo under the hood.
