A React app on your website
webmati add react-app and webmati app deploy — build a React/Vite app on your machine, deploy the build, mount it on any page; versions, rollback, deep links and a live preview.
Build the app on your machine; the website serves the build. Chunks, cache
headers, versions and client-side routes are the platform's job: you deploy
dist/, and a page mounts it with one tag.
webmati add react-app dashboard --route /dashboard # app/ and pages/dashboard.json
cd app && npm install && npm run build # dist/ + dist/.vite/manifest.json
cd .. && webmati app deploy # upload what changed, make it live
webmati push # the page (a draft until published)
webmati tool call publish_page --json '{"id":"dashboard"}'
Then open /pages/dashboard on the website — or any /dashboard/… address
(the route).
1. Scaffold: add react-app
webmati add react-app dashboard --route /dashboard
It writes:
app/— a Vite + React + TypeScript app built for the website:vite.config.ts(base: "./",build.manifest: true,src/main.tsxas the entry),src/config.ts(what the page tells the app),src/api.ts(the website's own backend: sign-in, routes, live updates), aREADME.md, andwebmati.app.json(name, build directory, page, route).pages/dashboard.json— a page with one App section (app-mount) that mounts it. It is pushed like any page.
The source in app/ is a project file (see
Your project's own files): push keeps it for your
team — never served — and the next developer's clone brings it. Its dist/ and
node_modules/ are never sent: visitors only ever get the build, through
app deploy. Another app goes elsewhere under app/:
webmati add react-app reports --into app/reports.
2. Deploy: app deploy
webmati app deploy
dashboard v3: live.
6 files (412.7 KB); uploaded 1 (2.1 KB) — the rest the website already had.
Served from /_app/dashboard/a1b2c3d4e5f6/ on the website's own domains, cached for a year (each version has its own URL).
Not deployed: index.html (the page that mounts the app is the theme's).
Route: every path under /dashboard renders /pages/dashboard.
Mount it on a page with {% app 'dashboard' %} or the app-mount section — https://shop.brand.com/pages/dashboard.
- Every file is hashed first; only what the website does not hold yet is uploaded (in parallel, retried), so a redeploy that changed one chunk sends one chunk. Nothing a visitor sees changes until the last step makes the version live.
--no-activateuploads without switching: release it later withwebmati app rollback dashboard <v>.--note "…"says what changed.--dist DIRdeploys another build directory;webmati app deploy <name>picks one app when the project has several.- Another bundler: pass its entries with
--manifest entries.json:{"entries":[{"file":"app.js","css":["app.css"],"imports":["chunk.js"]}]}.
What a deploy may carry: js mjs css map wasm json svg png jpg jpeg gif webp avif ico woff woff2 ttf otf txt webmanifest — never .html, .htm or .xml (the
page is the theme's). At most 10 MB a file, 50 MB and 2000 files a version. A
build that breaks a rule is refused before anything is sent.
3. Mount it on a page
In the theme, or any section:
{% app 'dashboard' %}
{% app 'dashboard', entry: 'src/admin.tsx', root: 'admin-root', loading: 'Loading…' %}
Or add the App section in the customizer (or a page's sections) and type the
app's name. The tag renders the live version: a root element, a JSON config next
to it, the stylesheets, <link rel="modulepreload"> for the chunks the entry
imports, and the entry as <script type="module">. An app that is not deployed
(or has nothing live) renders an HTML comment saying why — never an error.
The config (src/config.ts reads it): the website's id and slug, the page's
language, api (the website's own backend), base_path (where a router is
rooted) and preview. It never names the visitor — the page is cached for
everyone; the app keeps its own session.
4. Deep links: app route
A client-side router links to /dashboard/orders/42; on a refresh the browser
asks the website for that address. A route makes every path under a prefix
render the app's page:
webmati app route dashboard /dashboard # or "route" in webmati.app.json — mounted on every deploy
webmati app unroute /dashboard
Root the router at config.base_path (<BrowserRouter basename={config.base_path}>).
A route is the last guess: a real page, post, product or redirect at the same
address answers first, and the platform's paths (/cart, /checkout,
/account, /api, /_app …) can never be one.
5. Versions and rollback
webmati app list
webmati app versions dashboard
webmati app rollback dashboard # the version before the live one (asks first)
webmati app rollback dashboard 4 --yes # a given version
webmati app rm dashboard # every version and route (asks first)
The website keeps the live version, the newest 10 and any version live in the last day — a page someone opened before a deploy still loads its chunks.
6. Live preview while you work: dev --app
webmati dev --app dashboard
It runs the app's build in watch mode (npm run watch; --no-app-build if you
run your own), deploys every build as a dev build and reloads the preview,
which shows that build. A dev build is never live: visitors keep the live
version until you app deploy.
7. Its backend
src/api.ts talks to the website's own backend (config.api) the way the
shop-backend-frontend skill says: sign-in buttons from /auth/config, the
session in the browser, every call through a workflow route
(api.call("/my/todos")), live updates with api.listen(channel, …).

