React Frontend
Wire SchemaClientProvider with Supabase JWT auth and React Query caching.
This guide wires SchemaClientProvider with a custom fetch that injects a Supabase JWT on every request and configures React Query caching for record data.
Full provider setup
// @noverify
"use client";
import { createClient } from "@/modules/supabase/client";
import { SchemaClientProvider } from "@stndrds/react";
import { FR_TRANSLATIONS } from "@stndrds/react/locales/fr";
import {
ArchitectPanelProvider,
CreateModalContent,
FileViewer,
type FileViewerProps,
Icon,
ModalStack,
Skeleton,
} from "@stndrds/ui";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { type ReactNode, useCallback, useMemo } from "react";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 10 * 60 * 1000, // 10 minutes
},
},
});
export function SchemaProvider({ children }: { children: ReactNode }) {
const supabase = useMemo(() => createClient(), []);
const customFetch = useCallback(
async (input: RequestInfo | URL, init?: RequestInit) => {
const {
data: { session },
} = await supabase.auth.getSession();
const headers = new Headers(init?.headers);
if (session?.access_token) {
headers.set("Authorization", `Bearer ${session.access_token}`);
}
return fetch(input, { ...init, headers });
},
[supabase],
);
const getAccessToken = useCallback(async () => {
const {
data: { session },
} = await supabase.auth.getSession();
return session?.access_token ?? null;
}, [supabase]);
return (
<QueryClientProvider client={queryClient}>
<SchemaClientProvider
queryClient={queryClient}
translations={FR_TRANSLATIONS}
config={{
baseUrl: process.env.NEXT_PUBLIC_API_URL!,
fetch: customFetch,
getAccessToken,
}}
ModalStack={ModalStack}
renderFileViewer={(props) => (
<FileViewer {...(props as FileViewerProps)} mode="modal" />
)}
renderIcon={(iconName) => (
<Icon
loader={<Skeleton className="size-4" />}
icon={iconName}
className="size-4 text-current"
/>
)}
renderModalContent={(props) => <CreateModalContent {...props} />}
placeholder={(attribute) => attribute.label}
>
<ArchitectPanelProvider className="h-screen">
{children}
</ArchitectPanelProvider>
</SchemaClientProvider>
</QueryClientProvider>
);
}The provider requires queryClient, translations, config, and ModalStack. The render props — renderFileViewer, renderIcon, renderModalContent, and placeholder — are optional but recommended: without them standards falls back to unstyled defaults.
Custom fetch — auth header
The customFetch callback runs on every API call made by the standards React hooks. It reads the active Supabase session and attaches the JWT as an Authorization: Bearer header. The backend validates this token with the Supabase JWT guard — no extra session store is needed. The getAccessToken callback serves the same token to features that need it outside of a fetch context (for example, realtime subscriptions).
Because supabase.auth.getSession() is a lightweight in-memory read when the session is cached, the overhead per request is negligible.
QueryClient defaults
The QueryClient is created once outside the component so it survives re-renders. A staleTime of 5 minutes means standards record data is considered fresh for that window — no background refetch unless the data genuinely ages out. A gcTime of 10 minutes keeps unused cache entries in memory long enough to serve instant back-navigation without a round trip. Adjust both values down if your application has high write concurrency and users need near-real-time reads.
Locales
@stndrds/react ships a French locale out of the box:
// @noverify
import { FR_TRANSLATIONS } from "@stndrds/react/locales/fr";Pass the translations object to the translations prop on SchemaClientProvider. For other languages, create a translations object that matches the Translations type exported from @stndrds/react and add it to your project.
Architect mode
ArchitectPanelProvider from @stndrds/ui enables the runtime schema editor — a panel where power users can add attributes, configure views, and adjust object settings without touching code. It is off by default in production and toggled via the useArchitectMode() hook:
// @noverify
import { useArchitectMode } from "@stndrds/ui";
const { enable, disable, isEnabled } = useArchitectMode();Wrap your layout with ArchitectPanelProvider to make the panel available anywhere in the tree. The className prop on the provider controls the panel's container dimensions — h-screen is typical for a full-height shell.