TanStack Start
TanStack Start applications can use Messagevisor in route loaders, server routes, and React components through the React provider.
Install#
$ npm install @messagevisor/sdk @messagevisor/react @messagevisor/module-icuServer helper#
Create a server-only helper that loads all supported locale datafiles into one SDK instance:
import { createMessagevisor, type Messagevisor } from "@messagevisor/sdk";import { createICUModule } from "@messagevisor/module-icu";const supportedLocales = ["en-US", "nl-NL"];let instancePromise: Promise<Messagevisor> | undefined;export async function getMessagevisor() { if (instancePromise) return instancePromise; instancePromise = Promise.all( supportedLocales.map((locale) => fetch(`https://cdn.yoursite.com/datafiles/messagevisor-web-${locale}.json`).then( (response) => response.json() ) ) ).then((datafiles) => { const m = createMessagevisor({ datafile: datafiles[0], modules: [createICUModule({ ignoreTags: false })], }); datafiles.slice(1).forEach((datafile) => m.setDatafile(datafile)); return m; }); return instancePromise;}Server functions and server routes pass locale per call. Do not call setLocale() or setContext() while handling requests.
Server function#
Use a TanStack Start server function when route loaders or components need translated data:
import { createServerFn } from "@tanstack/react-start";import { getMessagevisor } from "./messagevisor.server";export const getDashboardCopy = createServerFn({ method: "GET" }) .inputValidator((data: { locale?: string; plan?: string }) => data) .handler(async ({ data }) => { const locale = data.locale || "en-US"; const m = await getMessagevisor(); return { title: m.translate( "dashboard.welcome", { name: "Ada" }, { locale, context: { platform: "web", plan: data.plan } } ), }; });Route loader#
Route loaders can resolve translated values before rendering:
import { createFileRoute } from "@tanstack/react-router";import { getDashboardCopy } from "../../messagevisor.functions";export const Route = createFileRoute("/$locale/")({ loader: ({ params }) => getDashboardCopy({ data: { locale: params.locale, plan: "pro", }, }), component: DashboardPage,});function DashboardPage() { const { title } = Route.useLoaderData(); return <h1>{title}</h1>;}Server routes#
TanStack Start server routes can return translated JSON from the same route tree:
import { createFileRoute } from "@tanstack/react-router";import { getMessagevisor } from "../../messagevisor.server";export const Route = createFileRoute("/api/welcome")({ server: { handlers: { GET: async ({ request }) => { const locale = new URL(request.url).searchParams.get("locale") || "en-US"; const m = await getMessagevisor(); return Response.json({ message: m.translate("dashboard.welcome", { name: "Ada" }, { locale }), }); }, }, },});React provider hydration#
For client-side hooks, load or fetch a datafile and pass an SDK instance to MessagevisorProvider:
import { useMemo } from "react";import { createMessagevisor } from "@messagevisor/sdk";import { createICUModule } from "@messagevisor/module-icu";import { MessagevisorProvider } from "@messagevisor/react";export function MessagevisorClientProvider(props: { datafile: any; children: React.ReactNode;}) { const m = useMemo( () => createMessagevisor({ datafile: props.datafile, modules: [createICUModule({ ignoreTags: false })], }), [props.datafile] ); return <MessagevisorProvider instance={m}>{props.children}</MessagevisorProvider>;}Then components below the provider can use React SDK hooks.
Client components do not need to pass locale per call. They receive one datafile for the current user-facing locale, and the provider instance keeps that locale as its active locale. If the user switches language in the browser, load the new datafile, call setDatafile(), then call setLocale().
Loading from a CDN#
If datafiles are published under a different path, update the CDN URL in your server helper:
const datafile = await fetch( `https://cdn.yoursite.com/datafiles/messagevisor-web-${locale}.json`).then((response) => response.json());
