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.