Standards Docs
Guides

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.

On this page