Developers
Open app

Capabilities

Single-page apps

Most of a site is best built as server-rendered pages — they're fast, indexable, and translated automatically. But for app-like surfaces — a dashboard, a client portal, an interactive tool — you can mount a single-page app (React, Vue, Svelte, anything that builds to static assets) under a path on the site, while the rest stays server-rendered. This path is CLI-only.

The model

You choose a path prefix — say /app or /portal — and configure it so that any URL under that prefix serves your app's shell. Your client-side router then takes over in the browser and renders the right view. Outside that prefix, the site behaves normally: server-rendered, translated, SEO-friendly pages.

Your app's built files live in the site's /assets/ folder and load by their own paths, so code-splitting and lazy chunks work as your bundler emits them.

Your toolchain, your repo

You build the app however you like — vite build, or whatever your framework uses — with the output directed into the site's /assets/ folder. Then you publish the site with the CLI. Only the built bundle is published; your framework source stays in your own repository. A typical loop:

# in your app's repo
npm run build            # output into the pulled site's /assets/

# in the pulled site
acira publish -m "Update the portal app"

If your bundler also emits the site's stylesheet, turn the platform's CSS build off in the site's root configuration — otherwise the platform regenerates the stylesheet at publish from the Tailwind classes in your server-rendered templates, replacing your bundler's output. The switch is site-wide, and the platform's build reads only your server-rendered templates and never your bundle, so a hybrid site has to pick a side: leave the build on and let your bundler emit a second stylesheet for the app half, or turn it off and have your bundler produce the whole thing.

Your bundler's public/ folder is the site's public folder: files in it serve at their own path locally and once published, identically. Because your output directory is the site's /assets/, your bundler will also copy public/ in there — a redundant second copy you can suppress (publicDir: false in Vite) or ignore.

Talking to the backend

Your app doesn't need a separate API. It calls the site's own page handlers as a same-origin JSON API — the handlers return JSON, your app fetches them — and it reuses the site's sessions, auth, database, and email unchanged. So a logged-in visitor in your SPA is the same session the rest of the site sees.

Tradeoffs to know

  • No auto-translation or SEO inside the SPA. Views rendered client-side aren't server-rendered, so their content isn't translated automatically and isn't indexed. Use SPA areas for app/portal surfaces; keep marketing and content as server-rendered pages.
  • Locale prefixes still reach inside the SPA. On a multilingual site every path under your prefix is also served under each language code — /de/app, /de/app/settings, and your JSON API at /de/app/api/… — and no setting excludes a folder from that. A router that hard-codes /app as its base misses every one of those URLs, so derive the base path from the URL at runtime; a hash router is unaffected.
  • Assets are public by filename. Anything sensitive must be a database attachment served through access control, never a file in /assets/.
  • CLI-only. The AI coding agent authors server-rendered pages, not framework builds — SPA areas are built and published with the CLI.

Previous

Assets, styling & theming

Next

Configuration