> ## 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.

# Overview

> Hold a front-end React single-page application in its own module — a Vite app on TanStack Router and Query, served to the browser.

A **spa module** holds a single-page application: the React app your users load in the browser. Unlike the other modules in your project, it carries no backend logic — no controllers, services, or repositories. It is a self-contained front end built with [Vite](https://vitejs.dev), [TanStack Router](https://tanstack.com/router) for file-based routing, and [TanStack Query](https://tanstack.com/query) for data fetching, written in React and TypeScript. It lives under `modules/<kebab-name>/` like any other module, and its `<name>.yml` declares `type: "spa"`.

## Why a spa module

* **Front-end, not backend.** A spa module is the browser application — the UI users see and interact with. It does not register controllers or run on the server alongside your API.
* **Vite-powered.** Each spa is a standalone Vite app with its own `dev`, `build`, and `preview` scripts and its own dev port, so you run and ship it independently.
* **TanStack Router + Query.** Routing is file-based (routes are folders and files under `src/routes/`), and server state is fetched and cached with TanStack Query — no manual route table, no ad-hoc fetch glue.
* **Composes a design module.** A spa builds its UI on top of a [design module](/design-system/overview) — the shared design system. The chosen design is recorded in the spa's yml, so components and tokens come from one place.
* **Feature-sliced.** Code is organized as vertical slices under `src/features/`, with cross-cutting code in `src/shared/`, so a domain's routes, components, and hooks live together.

## How it differs from a backend module

A regular [module](/module/overview) (or a microservice) is server-side: it registers into `AppModule` or `SharedModule`, exposes path aliases in the root `tsconfig`, and runs as part of the API. A spa module does none of that — it is a browser app that the API serves to the client.

| Aspect           | Backend module / microservice                | Spa module                           |
| ---------------- | -------------------------------------------- | ------------------------------------ |
| Runs             | On the server, as part of the API            | In the browser                       |
| Registration     | Registered into `AppModule` / `SharedModule` | Not registered                       |
| tsconfig aliases | Added to the root `tsconfig` paths           | Not added                            |
| Build & serve    | Bundled with the app                         | Built by Vite, served to the browser |
| Stack            | Controllers, services, repositories          | React, Vite, TanStack Router/Query   |

## How it works

`ooneex spa:create` scaffolds a base module, marks it `type: "spa"`, and clones the upstream [`skeleton-spa`](https://github.com/ooneex/skeleton-spa.git) repository into the module's `src/`. It then installs the spa dependencies and assigns a free dev port — `5000` by default, or the next free port if that is taken — by adding `dev`, `build`, and `preview` Vite scripts to the module's `package.json`. During creation you also pick the [design module](/design-system/overview) the spa composes (an existing `type: "design"` module or a new one), recorded as `design: "<kebab>"` in the spa's yml.

| Top-level folder | What it holds                                                                    |
| ---------------- | -------------------------------------------------------------------------------- |
| `public/`        | Static files served verbatim (favicons, images, robots.txt).                     |
| `src/bootstrap/` | The entry point — `index.html`, `app.tsx`, and the generated `routeTree.gen.ts`. |
| `src/routes/`    | File-based routes; the folder and file layout is the URL structure.              |
| `src/features/`  | Vertical slices, one per domain — routes, components, and hooks for a feature.   |
| `src/shared/`    | Cross-feature code shared across the whole app.                                  |

A spa module's tree therefore looks like this:

```text theme={null}
modules/dashboard/
├── dashboard.yml          # type: "spa", design: "<kebab>"
├── package.json           # dev/build/preview Vite scripts + dev port
└── src/                   # cloned from skeleton-spa
    ├── public/
    ├── bootstrap/
    │   ├── index.html
    │   ├── app.tsx
    │   └── routeTree.gen.ts
    ├── routes/
    ├── features/
    └── shared/
```

## Working with a spa

Once created, you build the app out with the generators:

* Add a feature slice with `ooneex spa:feature:create` — see [Features](/spa/features).
* Add a translation hook with `ooneex translation:create` — see [Internationalization](/spa/internationalization).
* Remove a spa with `ooneex spa:remove` — see [Remove](/spa/remove).

## Next steps

* [Structure](/spa/structure) — the full layout of a spa module.
* [Features](/spa/features) — vertical slices and `spa:feature:create`.
* [Routing](/spa/routing) — file-based routing with TanStack Router.
* [Data fetching](/spa/data-fetching) — server state with TanStack Query.
* [Internationalization](/spa/internationalization) — translation hooks in a spa.
* [Create](/spa/create) — `spa:create` in full.
* [Remove](/spa/remove) — `spa:remove` in full.
* [Design System](/design-system/overview) — the design module a spa composes.
* [Modules](/module/overview) — how backend modules differ.
