Iridel Standards

Project Directory Structure

We follow a directory inspired by slice-based architecture, and tailor fit to our needs.

This is a modified version of slice-based architecture and Bulletproof React.

Root-level Directories

  • public for assets
  • scripts for anything scriptable (ie. seeding test data or copying files on build)
  • server aliased in tsconfig as @server for server-side code if we decide to have the server & frontend on the same repository
  • src
    • app for Next.js routing
      • it should only contain page.tsx and the other files.
      • do not mix components & other files there
    • components for global, shared components
      • ui for shadcn components
      • layout for layout.tsx components
      • pages for error.tsx and loading.tsx pages
    • config for single source of truth configuration
      • app.config.ts
        • OG metadata
        • title, description, etc.
      • env.client.config.ts
        • NEXT_PUBLIC_ process.env variables
      • env.server.config.ts
        • any other process.env that shouldn't be shown in the client
    • constants
      • for stuff like UNKNOWN_ICON_IMG_SRC to centralize string definitions to not repeat ourselves
      • rarely used
    • hooks
      • global hooks like use-is-mobile.hook.ts
    • lib
      • for integrations with packages like auth, i18n, payload, db, dexie, etc.
    • providers
      • for projecting React Context across many components
      • query-client.provider.tsx for tanstack query
    • stores
      • for globally-scoped stores that are not scoped by feature
      • site-data.store.ts
    • styles
      • colors.css color theme definitions
      • fonts.css for fonts
      • globals.css for everything else + any third-party CSS imports like tw-animate-css

Feature-based Directories

We group features into their own directories inside src and never keep them at the root unless they are global & reusable.

For example:

  • src/features/achievements
    • components
      • achievement-group-card.tsx
    • hooks
      • use-achievements.ts
        • useShallow zustand hook to abstract the method of a zustand slice store
    • services
      • achievements.service.ts
        • fetches data for rendering components in an RSC
    • slices
      • achievements.slice.ts
        • a composable zustand slice that we can use in a global store or with other features to compose bigger stores
    • types
      • achievement-category-enum.type.ts
        • exports const AchievementCategoryEnumSchema and type AchievementCategoryEnum = z.infer<typeof AchievementCategoryEnumSchema>
    • utils
      • get-achievement-category-image.ts

We can then import these features in other parts of the application with proper aliasing.

  • import { type AchievementCategoryEnum } from @/features/achievements/types/achievement-category-enum.type