Skip to content

Frontend Architecture

React 18.2 + Vite 4.4, JSX only (no TypeScript). Tailwind for styling. The import alias @src/.

State strategy

ConcernApproach
Server readsCustom hooks (useState + useEffect + useRef) calling supabase.rpc(), cached via @/lib/cache. Not TanStack Query.
MutationsTanStack Query useMutation (used for EF write calls, primarily in Edge Builder).
FormsReact Hook Form 7.49.
ThemeuseTheme(){ isLight }, applied with cn().
AuthuseAuth() from SupabaseAuthContext.
Feature flagsuseFeatureGate() from FeatureGateContext.

Component rules

  • Default exports for page/feature components; named exports for shared UI primitives.
  • Props destructured in the signature — never props.x in the body.
  • No data fetching in components — always delegate to a hook.
  • No supabase.* calls in components — they are pure UI consumers.
  • Import UI from your tier's components/ui/, not global ui/ directly.

Hook rules

One data concern per hook (one RPC per hook). Return the consistent shape { data, loading, error, refetch }. CACHE_KEY is a module-level constant. A hasLoaded ref flips loading to false only once, so background refreshes don't flicker the spinner. See the canonical pattern in Market Mood and Data Access Strategy.

Routing

Routes are defined as route objects in src/components/routes/AppRoutesConfig.jsx (and AdminRoutesConfig.jsx), with pages lazily imported via React.lazy. The full path → component → source mapping is auto-generated: Route Map.

UI Hybrid System

Two layers:

src/components/ui/                 ← GLOBAL PRIMITIVES (tier-agnostic, zero tier logic)
src/tiers/users/components/ui/     ← USER overrides (orange/dark theme)
src/tiers/admin/components/ui/     ← ADMIN overrides (blue/light theme)
src/tiers/public/components/ui/    ← PUBLIC overrides
src/tiers/landing/components/ui/   ← LANDING overrides
  • Feature components import from their tier's components/ui/.
  • Tier overrides wrap the global primitive: import { Button as GlobalButton } from '@/components/ui/button'.
  • Consistent across tiers (never tier-specific): border radius, typography scale, spacing, focus states, animation timing, elevation.
  • May differ per tier: primary color, light/dark theme, hover intensities, branding.

Theming (component-first, mandatory for new code)

Every component handles dark and light mode with the cn() + isLight guard:

jsx
import { useTheme } from '@/contexts/ThemeContext';
import { cn } from '@/lib/utils';

const { isLight } = useTheme();
className={cn('base', isLight ? 'bg-white text-gray-900' : 'bg-slate-800 text-white')}

Never hardcode hex colors in class strings (charts are the only exception). Never import the legacy @/theme/lightTheme / darkTheme. The full design system lives in documentations/01-layout/guidelines/niftytoolkit-uiux-theme-guidelines.md.