Reference mode: a static site, or a server-rendered app in another language
Read when SKILL.md step 1 resolves to a stack that cannot run app/. <SKILL_ROOT> is the
installed skill folder; the shipped code is under <SKILL_ROOT>/templates/. Steps 2 to 5 of
SKILL.md still apply; this file is the mechanics that differ.
The data layer ships a second time as a REST layer: templates/shared/rest/ (the auth seam
client.ts, media.ts, config.ts) and templates/<vertical>/rest/ (the same exports as the
vertical's app/wix/<vertical>/ data layer, over fetch), typed against the same types.ts and
importing the same *-core.ts rule files as the SDK layer — one implementation of the rules, two
transports. The vertical's INSTRUCTIONS.md names its modules and what each surface does with
them; this section is the mechanics, the same for every vertical. The site-search capability
(templates/shared/capabilities/site-search/, on from plan.capabilities.siteSearch) has the same
two transports: with --plan its REST twin and stores land in js/wix/ beside the vertical's
(search.js, search-store.js, suggest-store.js), and its INSTRUCTIONS.md applies.
- Static site (no bundler). Step 3's setup with
--stack staticrunsinitin the project folder (site, OAuth app,wix.config.json), pointssite.outputDirectoryat"./site", and runsdeploy.mjs <vertical> --stack static --out site, which composes the REST layer and the vertical's framework-free stores flat intosite/js/wix/and strips them to browser ESM (comments kept, the.tskept beside the.jsto read). The site lives in that subfolder,site/, holding only the pages, styles, assets andjs/:wix releaseuploads it whole, so the project root (config,plan.json, seed output, the skills) must not be it; move the pages in. (On a project that already has its config, the samedeploy.mjscall does the deploy.) Pages import the vertical's modules from./js/wix/in a<script type="module">: the stores hold the state machines (subscribe, render fromgetState(), call actions), the page holds the rendering. The visitor token lives inlocalStorageand is the visitor's identity across Wix — never mint one per page. A route is a page plus a query-string slug (item.html?slug=…). Wix static hosting serves files, not directories:/shopdoes not resolve toshop/index.html, and there is no routes configuration — name the file and link to it. Seed per the vertical'sSEED.md(Node + the CLI token, no project dependencies). Release withnpx @wix/cli@latest release— no build. Item-page tags come from the entity'sseoData, set after the fetch (document.title, the meta description). - Server-rendered, another language (Flask, Laravel, Rails, …). The same shape as managed
Astro — pages rendered on the server, the interactive surfaces in the browser — with hosting and
SEO plumbing theirs.
initstill runs in the project folder; rundeploy.mjs <vertical> --stack staticthere too: it only needswix.config.jsonand writesjs/wix/. Split by where the call runs:- Reads render on the server. Port the vertical's
rest/read module and its*-core.tsto the server language: each function is one HTTP call with a literal URL and JSON body, and the core carries the rules. That code is tested and proven against live sites — carry its bodies over as they are,fieldsarrays and filter keys included (they are not guessable, and a near-miss returns empty or unformatted data with no error), and render what Wix returns (formattedAmount, never a number you format yourself). The shipped JS runs: when in doubt, run the module with Node against the same site and compare one entity with your port. Public reads need no visitor identity — one anonymous visitor token per server process, refreshed perclient.ts, is enough for them. - Visitor-specific state runs in the browser. Whatever the vertical does on the visitor's
behalf (a store's cart and checkout, a booking, an RSVP, a form submit) loads the vertical's
js/wix/module in the templates and talks to Wix from the page, exactly as a static site does: the browser owns the visitor token inlocalStorage, so the server handles no per-visitor tokens. If that state must run server-side anyway,client.ts's header applies: one token set per visitor in the visitor's session, never one process-wide token (that is one identity shared by everyone). - A Wix-hosted flow returns to the origin that started it, and only to one it knows.
Checkout, a booking payment, a plan purchase all open on Wix and come back to your server;
the return works only for an origin on the site's OAuth app allow-list. Add the server's
origin — the dev one while verifying (
http://localhost:<port>), the public https one when it goes live — before the first checkout test: wix-manage's Manage OAuth Apps recipe, "Update an OAuth App", fieldallowedRedirectDomains, on the app whose id isappIdinwix.config.json. Without it the Wix page opens and cannot return. Do it; do not only say it. - Pre-rendered → Wix-hosted. If the project builds to static HTML (Frozen-Flask, Pelican,
Hugo, Eleventy, any static-site generator), Wix can host the output: run
deploy.mjs <vertical> --stack static --out <build dir>sojs/wix/lands inside the build output (or copy it there after each build), make the generator emit a page for every entity slug the vertical's list read returns (walk it by cursor, never only the first page), pointsite.outputDirectoryat the build folder,wix release. The build's own reads use one anonymous visitor token for the duration of the build. What Wix hosts is exactly the contents of that folder after your last build, served as files: every asset a page references must be in there and current — if the pipeline has more than one build step (templates, then a CSS or asset bundle), they all run, in order, on every rebuild, or the release carries a stale piece. a clean path does not resolve to a folder'sindex.html; name the file and link to it. Generated pages sit at different depths, so referencejs/wix/through one base path (a template variable, or root-relative/js/wix/…), never./js/wix/— a relative path breaks one level down. The generated page is the first paint, not the whole surface: the vertical's interactive behaviour (a store's sort, filters, and cart; a blog's search; a booking flow) still runs client-side on top of it from the samejs/wix/modules, so the vertical's surface contracts inINSTRUCTIONS.mdapply unchanged. Close with the rebuild + release command and one line for the owner: content edits made in the dashboard reach the site when that command runs; the browser-side flows are live regardless. A running server (live reads on every request) stays theirs to host. Then read the vertical'sINSTRUCTIONS.mdfor the surfaces and hard rules, and the sharedDESIGN.md/CONTENT.md. Before writing any surface, read the vertical's shipped hooks and components — itsINSTRUCTIONS.mdlists which files and what to take from each. They don't deploy on this stack, and they are working, tested code for exactly the behaviour you are about to write in your own; rewriting them from prose is where the bugs come from (the runs that skipped them shipped a broken quick-add, the run that read them didn't). Both were written for the stacks that receive the code, so they speak that stack's dialect: the Tailwind classes in the skeletons and the components are one spelling of layout and behaviour rules that hold everywhere (a bounded image band on phones, name and price on separate lines, the buy control pinned to the tile's bottom, an overlay that locks scroll and returns focus). Take the rules; write them in the CSS your stack uses, on a token set you define — nothing here asks you to add Tailwind. Close with run (or rebuild) instructions, the live URL when Wix hosts the output, the dashboard link, and — when hosting is theirs — which origins are on the OAuth app's allow-list and that the public one must be added when the server moves (the step above).
- Reads render on the server. Port the vertical's
- Both: the calls in
rest/are the ones a visitor token may make from a page — public reads and the visitor's own actions. Anything elevated (writes to content, other people's data) runs server-side pertemplates/shared/CUSTOM_OPERATIONS.md; the seed's CLI token never belongs in a page. A static site has no SSR; neither case has owner-editable item-page SEO through@wix/seo(tags come from the entity'sseoData) — say so in the closing message; managed Astro stays the recommendation for a public site.