Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Jason-AML/MonzaSport-Nextjs/llms.txt

Use this file to discover all available pages before exploring further.

Three React context providers wrap the entire application inside src/app/layout.js: I18nProvider (translations), QueryProvider (TanStack React Query), and AuthProvider (Supabase session). They compose from the outside in — each inner provider can safely consume the context of any provider that wraps it, making the dependency order deliberate and important.

Provider Stack

The root layout is an async Server Component that fetches the current user before rendering. The resolved user object is threaded down as a prop to AuthProvider, eliminating any client-side loading flash:
src/app/layout.js
export default async function RootLayout({ children }) {
  const user = await getUser();
  return (
    <html lang="es">
      <body>
        <I18nProvider>
          <Analytics />
          <QueryProvider>
            <AuthProvider initialUser={user}>
              <main>{children}</main>
              <FloatingBar />
              <Footer />
              <Navbar />
            </AuthProvider>
          </QueryProvider>
        </I18nProvider>
      </body>
    </html>
  );
}
Why this order matters:
  • I18nProvider is outermost so that every component in the tree — including login forms, the navbar, and error pages — has translation strings available from the very first render.
  • QueryProvider sits in the middle, making the shared QueryClient instance available to any client component that calls useQuery or useMutation, including those inside AuthProvider.
  • AuthProvider is innermost so it can leverage React Query if needed for session-aware data fetching, while still being wrapped by translations for any auth-related UI text.
Navbar, Footer, and FloatingBar are placed inside AuthProvider so they can call useAuth() to conditionally render user-specific UI — such as a profile dropdown when the user is signed in.

AuthProvider

File: src/providers/AuthProvider.jsx AuthProvider manages the Supabase session for the entire application. It is a 'use client' component that accepts the server-resolved user as initialUser, then subscribes to supabase.auth.onAuthStateChange to keep the local state in sync with any subsequent sign-in or sign-out events.

How it works

1

Server-side prefetch

getUser() runs in the Server Component root layout via @/lib/server. The resolved user (or null) is passed as initialUser to AuthProvider before the page is sent to the browser — no client-side loading state is needed on first paint.
2

State initialisation

useState(initialUser) seeds the auth state immediately. If initialUser is non-null, loading starts as false, so components never see an intermediate “loading” flash.
3

Realtime subscription

useEffect calls supabase.auth.onAuthStateChange, which fires whenever the session changes (sign-in, sign-out, token refresh). The callback updates user and sets loading to false.
4

Cleanup

The effect returns subscription.unsubscribe() so the listener is removed when the provider unmounts — preventing memory leaks.

Source

src/providers/AuthProvider.jsx
"use client";

import { createClient } from "@/lib/client";
import { createContext, useContext, useEffect, useState } from "react";

const AuthContext = createContext({ user: null, loading: true });

export function AuthProvider({ children, initialUser }) {
  const [user, setUser] = useState(initialUser);
  const [loading, setLoading] = useState(!initialUser);
  const supabase = createClient();

  useEffect(() => {
    const {
      data: { subscription },
    } = supabase.auth.onAuthStateChange((event, session) => {
      setUser(session?.user ?? null);
      setLoading(false);
    });
    return () => subscription.unsubscribe();
  }, []);

  return (
    <AuthContext.Provider value={{ user, loading }}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => useContext(AuthContext);

Consuming the context

import { useAuth } from '@/providers/AuthProvider';

export default function ProfileButton() {
  const { user, loading } = useAuth();

  if (loading) return null;
  return user ? <UserMenu user={user} /> : <LoginLink />;
}

QueryProvider

File: src/providers/QueryProvider.jsx QueryProvider wraps the app with QueryClientProvider from TanStack React Query v5. It creates a single shared QueryClient instance (via useState to prevent re-creation on re-renders) and mounts the React Query Devtools in development.

Configuration

src/providers/QueryProvider.jsx
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { useState } from "react";

export default function QueryProvider({ children }) {
  const [queryClient] = useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            staleTime: 60 * 1000,       // data is fresh for 60 seconds
            refetchOnWindowFocus: false, // no background refetch on tab focus
          },
        },
      })
  );

  return (
    <QueryClientProvider client={queryClient}>
      {children}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}
Default OptionValueEffect
staleTime60 000 msCached vehicle data is considered fresh for one minute before a background refetch is triggered
refetchOnWindowFocusfalseSwitching browser tabs does not trigger a refetch, avoiding unnecessary Supabase reads
ReactQueryDevtools renders only in development builds (it is tree-shaken in production). Open the devtools panel in the browser to inspect active queries, cached data, and mutation status in real time.

I18nProvider

File: src/providers/I18nProvider.jsx I18nProvider initializes i18next and exposes the configured instance to every component via I18nextProvider. Because i18next initialization is a side-effect that must run in the browser, the provider is marked 'use client' and imports @/i18n/index to trigger the setup.

Initialization

src/i18n/index.js
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import es from "./translations/es";
import en from "./translations/en";

i18n.use(initReactI18next).init({
  resources: {
    es: { translation: es },
    en: { translation: en },
  },
  lng: "es",          // Spanish is the default language
  fallbackLng: "en",  // fall back to English if a key is missing in Spanish
  interpolation: {
    escapeValue: false, // React already escapes values
  },
});

export default i18n;

Provider component

src/providers/I18nProvider.jsx
"use client";

import "@/i18n/index"; // triggers initialization on the client
import { I18nextProvider } from "react-i18next";
import i18n from "@/i18n/index";

export default function I18nProvider({ children }) {
  return (
    <I18nextProvider i18n={i18n}>
      {children}
    </I18nextProvider>
  );
}

Translation key namespaces

Translations are organized by feature in both en.js and es.js:
Namespace keyUsed by
navNavbar — navigation links and register button
floatingBarFloatingBar — help text and concierge CTA
profile_dropdownNavbar — authenticated user menu
home_heroHeroVideo — headline, tagline, CTA buttons
loginLoginForm — labels, placeholders, links
registerRegisterForm — labels, validation toasts
categorie_splitCategorySplit — cars and bikes section copy
testimonialsTestimonials — review text and roles
latest_newsLatestNews — article titles and tags
footerFooter — links, newsletter, legal text

Consuming translations in a component

import { useTranslation } from 'react-i18next';

export default function HeroVideo() {
  const { t } = useTranslation();
  return (
    <h1>{t('home_hero.title')}</h1>
  );
}
The root <html> element in layout.js has lang="es" set statically. If you extend the app with full locale-aware routing, update this attribute dynamically based on the active i18next language.

Supabase Clients

Two Supabase client factories live in src/lib/ and serve distinct rendering environments. Both export a function named createClient, so the correct file must always be imported explicitly.

src/lib/client.ts — Browser client

src/lib/client.ts
import { createBrowserClient } from '@supabase/ssr'

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )
}
Use this in any 'use client' component or custom hook. It reads the public environment variables and creates a session-aware client backed by browser storage.

src/lib/server.ts — Server client

src/lib/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'

export async function createClient() {
  const cookieStore = await cookies()
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll: () => cookieStore.getAll(),
        setAll: (cookiesToSet) => {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options)
            )
          } catch {
            // Server Component context — session is handled by middleware
          }
        },
      },
    }
  )
}
Use this in Server Components, API route handlers, and src/services/auth/auth.server.js. It reads and writes cookies via the Next.js cookies() API, allowing the server to access and refresh the user’s Supabase session.
Both client.ts and server.ts export a function called createClient. Always import from the correct path. Importing @/lib/client in a Server Component will throw a runtime error because createBrowserClient relies on browser APIs. Importing @/lib/server in a 'use client' file will fail because next/headers is not available on the client.

Quick reference

Import pathEnvironmentUsed in
@/lib/clientBrowserAuthProvider, useChat, collectionClient.js, auth.client.js
@/lib/serverServerauth.server.js, collections.js, API route handlers

Build docs developers (and LLMs) love