Skip to main content
apps/web is Ship’s front-end: a TanStack Start application running in SPA mode. It’s the same base whether you scaffolded full-stack or web-only — what changes is where the data comes from. The Ship web app

The stack

The whole app is wired in vite.config.ts with the tanstackStart plugin in spa mode, plus @tailwindcss/vite and vite-tsconfig-paths (the @/... alias):
vite.config.ts
SPA mode does not mean “no server.” TanStack Start still runs a server — it just doesn’t server-render your routes. That server is where server functions execute, which is how the web-only shape does its backend work.

The root

Everything hangs off src/routes/__root.tsx. It defines the document shell, the head (meta, fonts, favicon), and the providers every route shares — TanStack Query, theming, tooltips, and the toaster:
src/routes/__root.tsx
How routes are organised under this root — files, params, guards, loaders — is its own page: Routing.

Two ways to get data

The base apps/web ships a landing page and nothing else to fetch. How you add data depends on the shape you scaffolded.

Full-stack

Talk to apps/api through a fully typed oRPC client. The client and the useApiQuery / useApiMutation / useApiForm hooks arrive with the Auth plugin, which also adds the sign-in/up pages and the authenticated app shell.

Web-only

No separate API. Backend logic runs as server functions (createServerFn) on the Start server, called straight from a route loader.

Full-stack: the typed oRPC client

Add the Auth plugin and your routes get an apiClient whose methods mirror the API contract one-to-one — every input and return type is inferred from the endpoint’s .input() / .output() schemas. Wrap a call in TanStack Query with the plugin’s hooks:
Types flow end-to-end with no codegen and no shared types package: the API emits .d.ts (pnpm --filter api build:types) and the web app imports them through its "api": "workspace:*" dependency. Change an endpoint’s output and the client’s types change on the next build. See How Ship works.

Web-only: server functions

With no apps/api, your backend is a createServerFn handler that runs on the Start server and is consumed by a route loader:
Full pattern — loaders, mutations, validation — on the Server functions page.

Styling

Tailwind v4 is wired through @tailwindcss/vite; tokens live in src/globals.css. UI primitives are shadcn/ui components copied into src/components/ui/** — your code, edit freely. Conditional classes use the cn() helper. See Styling.

Run it

Where things live

Next

  • Routing — file-based routes, params, guards and loaders.
  • Server functions — backend logic for the web-only shape.
  • Styling — Tailwind v4 and shadcn/ui.