Skip to main content
Localizia

Project structure

The layout

src/
  app/[locale]/       # All pages, localized
    (auth)/           # Sign-in, sign-up, verification, password reset…
    (marketing)/      # Public: home, pricing, changelog, legal
    (app)/            # Authenticated shell (sidebar)
      (org)/          # Org-scoped: dashboard, settings, billing, members, credits
      (user)/         # User-scoped: account profile, security, sessions
      admin/          # Platform admin
    api/              # Auth, tRPC, webhooks, storage route handlers
  config/             # Feature flags (*.config.ts)
  server/             # tRPC — server-only
    routers/          # One file per domain
    procedures.ts     # Procedure tiers + middleware
  lib/                # auth, billing, payment, email, storage, audit, db, logger
  components/
    ui/               # The kit's own unstyled component library
    icons/            # Curated icon re-export
    [feature]/        # Feature components (auth/, billing/, org/…)
  emails/             # React Email templates
  trpc/               # Client bindings + server caller
  env.ts              # Zod-validated env — single source of truth
  i18n/               # Routing, request config, navigation helpers
messages/             # en.json, es.json
prisma/               # schema.prisma, migrations, seed

The path alias @/* maps to src/*.

Conventions that matter

  • Files and folders are kebab-case. React component files are PascalCase matching their export (UserAvatar.tsx).
  • .server.ts suffix marks modules that must never reach a client bundle (Prisma access, secrets, email sending).
  • Route groups (auth), (app), (org) organize without affecting the URL.
  • Feature components go in src/components/[feature]/, never inside src/components/ui/.

Boundaries the tooling enforces

Some rules are enforced by ESLint, not just convention:

  • Prisma is only imported inside src/server/routers/. Components, pages, lib utilities, and config never touch the database directly.
  • Feature code imports UI from @/components/ui, never radix-ui directly.
  • Icons come from @/components/icons, never lucide-react directly.
  • All code reads env from @/env, never process.env.

These boundaries are what keep the layers swappable and the data access safe — see The tRPC API and UI & styling.