Docs /frontend/README

Frontend Documentation

React Router v7 web application

Overview

The Izri frontend is built with React Router v7 (formerly Remix), React 19, and Vite. It uses TanStack Query and tRPC for data, Tailwind v4 for styling, and shadcn/ui for components.

Technology Stack

  • Framework: React Router v7 + React 19
  • Build Tool: Vite 5
  • Styling: Tailwind CSS v4
  • UI Components: shadcn/ui (Radix primitives)
  • Data: tRPC + TanStack Query
  • Type Safety: TypeScript 5

Documentation Pages

  • Setup & Configuration: ./setup.md
  • Components & Forms: ./components-and-forms.md
  • Testing: ./testing-frontend.md

Application Structure

apps/web/
├── app/
│   ├── root.tsx                      # Root layout, providers
│   ├── routes.ts                     # Route configuration (authoritative)
│   ├── routes/
│   │   ├── home.tsx                  # Landing page (/)
│   │   ├── _auth.tsx                 # Auth layout
│   │   ├── _organizations.tsx        # Organizations layout (auth-protected)
│   │   ├── auth/
│   │   │   ├── login.tsx             # /auth/login
│   │   │   ├── register.tsx          # /auth/register
│   │   │   └── logout.tsx            # /auth/logout (outside auth layout)
│   │   └── organizations/
│   │       ├── index.tsx             # /organizations
│   │       ├── new.tsx               # /organizations/new
│   │       ├── $slug.tsx             # Layout for slugged routes
│   │       └── $slug/
│   │           ├── index.tsx         # /organizations/:slug
│   │           ├── dashboard.tsx     # /organizations/:slug/dashboard
│   │           ├── stats.tsx         # /organizations/:slug/stats
│   │           ├── projects/
│   │           │   ├── index.tsx     # /organizations/:slug/projects
│   │           │   ├── new.tsx       # /organizations/:slug/projects/new
│   │           │   └── $id.tsx       # /organizations/:slug/projects/:id
│   │           ├── settings.tsx      # /organizations/:slug/settings
│   │           ├── members.tsx       # /organizations/:slug/members
│   │           └── invite.tsx        # /organizations/:slug/invite
│   ├── providers/
│   │   └── trpc-provider.tsx         # tRPC client + QueryClient
│   ├── lib/
│   │   ├── trpc.ts                   # createTRPCContext React helpers
│   │   ├── trpc-server.ts            # SSR caller for loaders
│   │   ├── auth-client.ts            # Better Auth client
│   │   ├── auth-server.ts            # Better Auth session on server
│   │   ├── auth-loader.ts            # requireAuth, redirectIfAuthenticated
│   │   ├── organization-loader.ts    # requireOrganization, admin checks
│   │   └── contexts/
│   │       └── organization-context.tsx
│   └── components/
│       ├── common/
│       │   └── data-boundary.tsx
│       ├── errors/
│       │   └── layout-error-boundary.tsx
│       └── layout/
│           └── sidebar.tsx

Root Providers

Mount providers in app/root.tsx:

// app/root.tsx (excerpt)
import { TRPCProvider } from '@/providers/trpc-provider'
import { OrganizationProvider } from '@/lib/contexts/organization-context'

export default function Root() {
  return (
    <TRPCProvider>
      <OrganizationProvider>
        <Outlet />
      </OrganizationProvider>
    </TRPCProvider>
  )
}

Data Fetching (tRPC + TanStack Query)

Use the typed helpers from createTRPCContext.

import { useTRPC } from '@/lib/trpc'
import { useQuery } from '@tanstack/react-query'

export function OrgsList({ userEmail }: { userEmail: string }) {
  const trpc = useTRPC()
  const { data, isPending } = useQuery({
    ...trpc.organizations.list.queryOptions({ userEmail }),
    enabled: !!userEmail,
  })
  if (isPending) return <div>Loading...</div>
  return (
    <ul>{data?.organizations.map((o) => (<li key={o.id}>{o.name}</li>))}</ul>
  )
}

Authentication Flow (server-first)

  • Server validates session in loaders via Better Auth (auth.api.getSession)
  • requireAuth() in layout loaders protects routes
  • redirectIfAuthenticated() guards auth pages
  • Organization access: requireOrganization(), requireOrganizationAdmin()

tRPC Client

See ./setup.md for tRPC client setup, queries, mutations, and caching patterns.

Troubleshooting

  • 401/redirect loops: ensure cookies are sent (credentials: 'include') and APP_URL/domain match
  • Type errors: verify @izri/trpc/routers import and provider mounting

Reading this with an agent? /docs/frontend/README.md serves the raw markdown.

All docs