| id | provider-setup |
|---|---|
| title | Provider Setup Guide |
| sidebar_label | Provider Setup |
| sidebar_position | 32 |
The template uses a layered provider architecture to supply global state, theming, error boundaries, and feature contexts to the entire application. All providers live in components/providers/ and are composed together in the root layout.
Providers wrap the application in nested React contexts. The barrel export at components/providers/index.ts re-exports:
QueryClientProvider-- React Query cache and devtoolsErrorProvider-- global error boundaryFilterProvider-- filter state for listing pagesThemeProvider-- dark/light/system themeLayoutProvider-- layout view state and editor contextConfirmProvider-- imperative confirmation dialogsNavigationProvider-- initial-load detection
Two additional providers (SettingsProvider and SettingsModalProvider) are used in specific subtrees rather than at the root.
File: components/providers/theme-provider.tsx
Wraps the app in next-themes with system-preference support:
import { ThemeProvider as NextThemeProvider } from 'next-themes';
export function ThemeProvider({ children }: PropsWithChildren) {
return (
<NextThemeProvider
enableSystem={true}
attribute="class"
defaultTheme="system"
>
{children}
</NextThemeProvider>
);
}attribute="class"-- toggles adarkclass on the root element so Tailwinddark:variants work.defaultTheme="system"-- respects the user's OS preference on first visit.enableSystem={true}-- listens forprefers-color-schememedia-query changes at runtime.
No configuration props are required; the provider reads from localStorage automatically.
File: components/providers/query-provider.tsx
Initialises a shared QueryClient with React Query and adds dev tools in development:
export function QueryClientProvider({ children, dehydratedState }: QueryProviderProps) {
const queryClientRef = React.useRef<QueryClient>(
getQueryClient() || new QueryClient()
);
return (
<ReactQueryClientProvider client={queryClientRef.current}>
{children}
{process.env.NODE_ENV === 'development' && (
<ReactQueryDevtools initialIsOpen={false} />
)}
</ReactQueryClientProvider>
);
}| Prop | Type | Description |
|---|---|---|
children |
ReactNode |
Child components |
dehydratedState |
unknown |
Optional server-side prefetched cache state |
Pass dehydratedState from a server component to hydrate the client-side cache with data already fetched on the server, avoiding a duplicate request on mount.
File: components/providers/settings-provider.tsx
Provides site-wide feature flags, header/footer configuration, and location settings through a single context.
interface SettingsContextValue {
// Feature flags from config
categoriesEnabled: boolean;
tagsEnabled: boolean;
companiesEnabled: boolean;
surveysEnabled: boolean;
// Data existence flags from the database
hasCategories: boolean;
hasTags: boolean;
hasCollections: boolean;
hasGlobalSurveys: boolean;
// Header controls
headerSettings: HeaderSettings;
// Footer controls
footerSettings: FooterSettings;
// Location / map settings (camelCase runtime type)
locationSettings: LocationSettings;
}When no configuration overrides are provided the header defaults to:
| Setting | Default |
|---|---|
submitEnabled |
true |
pricingEnabled |
true |
layoutEnabled |
true |
languageEnabled |
true |
themeEnabled |
true |
moreEnabled |
true |
settingsEnabled |
true |
layoutDefault |
'home1' |
paginationDefault |
'standard' |
themeDefault |
'light' |
import { useSettings } from '@/components/providers/settings-provider';
function MyComponent() {
const { categoriesEnabled, headerSettings, locationSettings } = useSettings();
if (!categoriesEnabled) return null;
// ...
}The hook provides a fallback object when called outside the provider tree, so components render safely during server-side rendering or in isolated tests.
File: components/providers/filter-provider.tsx
A thin wrapper that delegates to FilterContextProvider from components/filters/context/filter-context. It manages the active category, tag, search query, and sort state for the main listing pages.
export function FilterProvider({ children }: PropsWithChildren) {
return <FilterContextProvider>{children}</FilterContextProvider>;
}Place this provider above any page that uses filter or search components.
File: components/providers/confirm-provider.tsx
Exposes an imperative confirm() function that returns a Promise<boolean>, replacing the native window.confirm with a styled modal dialog.
import { useConfirm } from '@/components/providers/confirm-provider';
function DeleteButton({ onDelete }: { onDelete: () => void }) {
const { confirm } = useConfirm();
const handleClick = async () => {
const ok = await confirm({
title: 'Delete Item',
message: 'This action cannot be undone. Continue?',
confirmText: 'Delete',
variant: 'danger',
});
if (ok) onDelete();
};
return <button onClick={handleClick}>Delete</button>;
}| Option | Type | Default | Description |
|---|---|---|---|
title |
string |
-- | Optional heading above the message |
message |
string |
required | Body text of the dialog |
confirmText |
string |
'Confirm' |
Label for the primary button |
cancelText |
string |
'Cancel' |
Label for the secondary button |
variant |
'danger' | 'warning' | 'info' |
'info' |
Controls icon and button colour |
The dialog renders at z-index: 9999 with a semi-transparent backdrop and includes an animated entrance.
File: components/providers/error-provider.tsx
Wraps children in the global ErrorBoundary component:
export function ErrorProvider({ children }: PropsWithChildren) {
return <ErrorBoundary>{children}</ErrorBoundary>;
}If any child component throws during rendering the boundary catches the error and displays a recovery UI.
File: components/providers/layout-provider.tsx
Composes two inner providers:
LayoutThemeProvider-- manages the active listing layout (grid, list, card, etc.) and persists the user's choice.EditorContextProvider-- provides editor state for the rich-text editing feature.
export function LayoutProvider({ children, configDefaults }: LayoutProviderProps) {
return (
<LayoutThemeProvider configDefaults={configDefaults}>
<EditorContextProvider>{children}</EditorContextProvider>
</LayoutThemeProvider>
);
}The optional configDefaults.defaultView prop sets the initial layout key before the user makes a selection.
File: components/providers/navigation-provider.tsx
Tracks whether the current render is the very first page load or a subsequent client-side navigation. This lets components (e.g. hero animations) run intro effects only once.
const { isInitialLoad } = useNavigation();Internally it watches usePathname() and flips isInitialLoad to false on the first path change.
File: components/providers/settings-modal-provider.tsx
Controls the visibility of a global settings/preferences modal. Features include:
- Focus management -- stores the previously focused element and restores it on close.
- Escape key -- pressing Escape closes the modal.
- Scroll lock --
document.body.style.overflowis set tohiddenwhile the modal is open.
const { isOpen, openModal, closeModal, toggleModal } = useContext(SettingsModalContext);A typical root layout nests the providers in this order:
<ThemeProvider>
<QueryClientProvider>
<ErrorProvider>
<NavigationProvider>
<SettingsProvider {...settingsProps}>
<ConfirmProvider>
<FilterProvider>
<LayoutProvider>
{children}
</LayoutProvider>
</FilterProvider>
</ConfirmProvider>
</SettingsProvider>
</NavigationProvider>
</ErrorProvider>
</QueryClientProvider>
</ThemeProvider>Order matters: ThemeProvider must be outermost so dark-mode classes are available to all children. QueryClientProvider should wrap anything that calls useQuery. ErrorProvider should be high enough to catch rendering errors from any inner provider.
- Theming Guide -- customising dark mode and colour tokens
- Configuration System -- how settings flow from YAML to providers
- Feature Flags -- toggling features consumed by SettingsProvider