AppProviders
Every React app needs a handful of providers at the top of the tree: data
caching, theming, internationalization, error capture. <AppProviders> wires
them all together in one declarative block — you say what you want enabled
and how to configure it, and the SDK builds the pyramid in the right order for
you. 🚀
The problem: the provider pyramid
Without <AppProviders>, the root of your app tends to become a hand-nested
pyramid. You have to remember which providers exist, the right nesting
order, and repeat it in every project:
// App.tsx — manual assembly (what we want to avoid)
import {
ErrorBoundary,
QueryProvider,
ThemeProvider,
I18nProvider,
AppRouter,
} from "tempest-react-sdk";
import { routes } from "@/routes";
export function App() {
return (
<ErrorBoundary fallback={<p>Something went wrong.</p>}>
<QueryProvider>
<ThemeProvider>
<I18nProvider locale="pt-BR" messages={messages}>
<AppRouter routes={routes} fallback={<p>Loading…</p>} />
</I18nProvider>
</ThemeProvider>
</QueryProvider>
</ErrorBoundary>
);
}
It works, but it's brittle: order matters (the ErrorBoundary must sit on the
outside to catch errors from the inner providers), the nesting grows diagonally,
and every app rewrites the same structure.
The providers still exist standalone
QueryProvider, ThemeProvider, I18nProvider, and ErrorBoundary remain
exported individually — use them directly when you need fine-grained control.
<AppProviders> is just the convenience that wires the four together for you.
The solution: a single block
// App.tsx
import { AppProviders, AppRouter } from "tempest-react-sdk";
import { routes } from "@/routes";
export function App() {
return (
<AppProviders errorBoundary={{ fallback: <p>Something went wrong.</p> }}>
<AppRouter routes={routes} fallback={<p>Loading…</p>} />
</AppProviders>
);
}
That's it. <AppProviders> nests everything outer to inner in this order:
ErrorBoundary → QueryProvider → ThemeProvider → I18nProvider → children
Notice that Query and Theme are already on with the SDK defaults — you
didn't have to configure anything. We only passed errorBoundary because it's
opt-in (see below).
Where <AppProviders> sits
The providers sit outside the routing; the <AppRouter> (and therefore
all your routes) sits inside. That way every page gets access to cache,
theme, and i18n, and any render error from any route lands in the
ErrorBoundary. See the routing page for details on <AppRouter>.
What's on by default vs. opt-in
| Prop | Default state | How to enable / configure |
|---|---|---|
query |
On | Already active. Pass an object to tune it. |
theme |
On | Already active. Pass an object to tune it. |
i18n |
Off (opt-in) | Pass { locale, messages } to mount it. |
errorBoundary |
Off (opt-in) | Pass { fallback } to mount it. |
queryandthemeare already active with the SDK defaults — the common case (you want data caching and theming) requires no configuration.i18nanderrorBoundaryonly enter the tree when you pass the matching prop. Omit it? The provider is simply not mounted.
Query defaults
When on by default, QueryProvider uses: a 5-minute staleTime, a
30-minute gcTime, retry: 1, and refetchOnWindowFocus: false.
Disabling a default with false
Sometimes the app already mounts its own QueryClient, or you don't want the
SDK's theme. Pass false to the prop to remove that provider from the tree:
// query and theme disabled — the app mounts its own outside
import { AppProviders } from "tempest-react-sdk";
export function App() {
return (
<AppProviders query={false} theme={false}>
<YourOwnProviders />
</AppProviders>
);
}
false removes the provider — it doesn't silently disable it
With query={false}, no QueryClient is mounted by <AppProviders>. If a
child component uses useQuery, it needs a provider mounted by you higher up
the tree — otherwise React Query throws at runtime.
Tuning each provider
Each prop accepts an object with the same options as the standalone provider
(minus children, which <AppProviders> controls).
Query
import { AppProviders } from "tempest-react-sdk";
<AppProviders query={{ defaultOptions: { queries: { retry: 3 } } }}>
<App />
</AppProviders>;
You can also pass an already-configured client (QueryClient) instead of
defaultOptions.
Theme
import { AppProviders } from "tempest-react-sdk";
<AppProviders theme={{ defaultTheme: "dark", storageKey: "my-app-theme" }}>
<App />
</AppProviders>;
i18n
import { AppProviders } from "tempest-react-sdk";
const messages = {
"pt-BR": { hello: "Olá" },
"en-US": { hello: "Hello" },
};
<AppProviders i18n={{ locale: "pt-BR", messages, fallbackLocale: "en-US" }}>
<App />
</AppProviders>;
i18n accepts locale, messages, and optionally fallbackLocale and
storageKey.
The ErrorBoundary fallback
The errorBoundary prop accepts fallback in two forms: a fixed ReactNode,
or a render function that receives { error, reset } — handy for showing
the error message and offering a "try again" button:
import { AppProviders } from "tempest-react-sdk";
<AppProviders
errorBoundary={{
fallback: ({ error, reset }) => <button onClick={reset}>{error.message}</button>,
}}
>
<App />
</AppProviders>;
Besides fallback, you can pass onError (callback on capture) and resetKeys
(values that, when they change, reset the boundary automatically).
Full example: all props together
Here's everything in action — tuned query, theme disabled, i18n mounted, and an error boundary with a render function:
import { AppProviders } from "tempest-react-sdk";
const messages = {
"pt-BR": { hello: "Olá" },
"en-US": { hello: "Hello" },
};
export function Root() {
return (
<AppProviders
query={{ defaultOptions: { queries: { retry: 3 } } }}
theme={false}
i18n={{ locale: "pt-BR", messages }}
errorBoundary={{
fallback: ({ error, reset }) => <button onClick={reset}>{error.message}</button>,
}}
>
<App />
</AppProviders>
);
}
Recap
<AppProviders>replaces the manual provider pyramid with one declarative block, nesting outer to inner:ErrorBoundary → QueryProvider → ThemeProvider → I18nProvider → children. ✅queryandthemeare on with the SDK defaults;i18nanderrorBoundaryare opt-in — they only mount when you pass the prop.- Pass
falsetoqueryorthemeto remove that provider (when the app mounts its own). - Pass an object to any prop to tune the matching provider.
- The error boundary
fallbackcan be aReactNodeor a function that receives{ error, reset }. - Place
<AppProviders>outside<AppRouter>: providers outside, routes inside. 💡