> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ooneex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Structure

> The full directory layout of a spa module — bootstrap, routes, feature slices, and shared code.

A spa module is a Vite-built React single-page app that lives under `modules/<name>/`. Its `<name>.yml` declares `type: "spa"` and records the design module it consumes with a `design:` line; its `package.json` (named `@module/<kebab>`) ships `dev`, `build`, and `preview` Vite scripts on the module's own free port (default `5000`). The `src/` tree is populated from the upstream `skeleton-spa` repo and follows a strict layering: routes stay thin, features own vertical slices, and `shared/` holds the code two or more features have in common.

## Directory layout

```
modules/<name>/                   # type: "spa"
  public/                         # Static files served verbatim — favicon, logos, robots.txt
  src/
    bootstrap/                    # Entry point and build wiring
      index.html                  #   HTML shell — mount node + script tag
      app.tsx                     #   Creates the router + React root and mounts it
      reportWebVitals.ts          #   Web-vitals (LCP, CLS, INP) collection hook
      routeTree.gen.ts            #   Auto-generated route tree — never edit
    routes/                       # File-based TanStack Router routes
      __root.tsx                  #   Root route: layout, error/not-found boundaries
      index.tsx                   #   Index (/) route — landing page
    features/                     # Vertical slices, one folder per domain feature
      <feature>/
        assets/                   #     Images/SVG/media used only by this feature
        components/               #     React components scoped to the feature
        hooks/                    #     The ONLY layer that talks to the backend
        layouts/                  #     Layout wrappers for this feature's pages
        services/                 #     Pure domain rules — never talks to backend
        store/                    #     Feature-local client state, not server data
        styles/                   #     CSS scoped to this feature
        translations/             #     i18n generated by translation:create
          translations.json
          use<Name>Translate.ts
        types/                    #     Feature domain types
        utils/                    #     Pure helpers within the feature
    shared/                       # Cross-feature code reused by >=2 features
      assets/  components/  hooks/  layouts/  services/  store/  styles/  types/  utils/
```

## Module-level files

Two files at the module root describe the spa to the framework and to Vite.

| File           | Purpose                                                                                                                                          |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `<kebab>.yml`  | Module manifest. Declares `type: "spa"` and a `design: "<kebab>"` line recording which design module this spa consumes.                          |
| `package.json` | Named `@module/<kebab>`. Exposes `dev`, `build`, and `preview` Vite scripts; the dev server runs on the module's own free port (default `5000`). |

See [Overview](/spa/overview) for how a spa fits into a workspace and pairs with a design module.

## Top-level folders

The `src/` tree is organized as four layers — entry wiring, routes, feature slices, and shared code — plus a `public/` directory served as-is.

| Folder           | What lives here                                                                                  | Notes                                                                                                        |
| ---------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| `public/`        | Static files copied verbatim to the web root — favicon, logos, `robots.txt`.                     | No bundling or hashing; reference by absolute path (`/favicon.svg`).                                         |
| `src/bootstrap/` | Entry point and build wiring: `index.html`, `app.tsx`, `reportWebVitals.ts`, `routeTree.gen.ts`. | Rarely edited by hand once scaffolded. `routeTree.gen.ts` is generated — never edit it.                      |
| `src/routes/`    | File-based [TanStack Router](/spa/routing) routes; each file maps to a URL path.                 | Keep thin — delegate UI to features, data to services/hooks.                                                 |
| `src/features/`  | Vertical slices, one folder per domain [feature](/spa/features).                                 | A feature owns its full stack; do not import another feature's internals — promote shared code to `shared/`. |
| `src/shared/`    | Cross-feature code reused by two or more features; same sub-layout as a feature.                 | The only place features import common code from.                                                             |

### Inside `bootstrap/`

| File                 | Role                                                                                                               |
| -------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `index.html`         | The HTML shell — a single document with the mount node and script tag. Edit it for top-level `<head>` tags.        |
| `app.tsx`            | Creates the router and the React root and mounts into the shell. Wire global providers (theme, query client) here. |
| `reportWebVitals.ts` | Web-vitals (LCP, CLS, INP) collection hook.                                                                        |
| `routeTree.gen.ts`   | The route tree auto-generated from `routes/`. Tooling output — never edit by hand.                                 |

### Inside `routes/`

| File         | Role                                                                        |
| ------------ | --------------------------------------------------------------------------- |
| `__root.tsx` | Root route: app-wide layout, error and not-found boundaries, shared chrome. |
| `index.tsx`  | The index (`/`) route — the landing page.                                   |

## Feature and shared sub-layers

Every `features/<feature>/` folder and the `shared/` folder share the same sub-layout. The layering is what keeps a spa maintainable: each sub-layer has one job, and the boundaries are enforced by convention.

| Sub-layer       | Purpose                                                                                            | Layering rule                                                         |
| --------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `assets/`       | Images, SVG, and media used by the feature.                                                        | Local to the feature; promote to `shared/assets/` only when reused.   |
| `components/`   | React components scoped to the feature.                                                            | Render UI; pull data from `hooks/`, never fetch directly.             |
| `hooks/`        | Data fetching, API calls, and local UI state.                                                      | The ONLY layer allowed to talk to the backend.                        |
| `layouts/`      | Layout wrappers for the feature's pages.                                                           | Compose `components/`; hold no domain logic.                          |
| `services/`     | Feature business logic — pure domain rules.                                                        | Never touches the backend; called by hooks and components.            |
| `store/`        | Feature-local client state.                                                                        | Client state only, not server data — server data belongs in `hooks/`. |
| `styles/`       | CSS scoped to the feature.                                                                         | Keep feature-specific; shared styles go in `shared/styles/`.          |
| `translations/` | i18n generated by `translation:create` — a `translations.json` and a `use<Name>Translate.ts` hook. | Generated; see [Internationalization](/spa/internationalization).     |
| `types/`        | Feature domain types.                                                                              | Shared types move up to `shared/types/`.                              |
| `utils/`        | Pure helpers within the feature.                                                                   | No side effects, no backend access.                                   |

<Note>
  The two rules to internalize: **hooks are the only layer that talks to the backend**, and **services hold pure domain logic and never touch the backend**. Keeping that split clean is what lets you test domain rules in isolation and swap data sources without rewriting your features. See [Data fetching](/spa/data-fetching) for how hooks call the backend.
</Note>

## The `shared/` folder

`shared/` mirrors a feature's sub-layout exactly — `assets`, `components`, `hooks`, `layouts`, `services`, `store`, `styles`, `types`, `utils` — and is the one place features import common code from. On create, the spa ensures every `shared/` sub-layer exists, each tracked with a `.gitkeep` so the empty directories survive in git.

`shared/hooks/` ships `useLang`, scaffolded alongside the first translation hook. It reads the `?lang=` query param (defaulting to `en`) so every feature resolves the active locale the same way.

<Note>
  A feature must never reach into another feature's internals. When two or more features need the same component, hook, type, or helper, promote it to the matching `shared/` sub-layer — that boundary is what stops a spa from collapsing into a tangle of cross-imports.
</Note>

The design tokens and UI primitives a spa consumes come from its paired design module — see [Design system structure](/design-system/structure) for that side of the layout, and [Features](/spa/features) for how to build out a feature slice.
