Publishing & frameworks
Build and share a working prototype with one command. After installing the CLI
and linking your project with artor init, run this from its folder:
artor publish # build and ship the next version → latest
artor publish --alias staging # publish to "staging"; see named-link behavior belowYou don’t edit config files, remember build flags, or pick a “deploy type”. Artor works out
what kind of app you built and prepares it for preview. Plain artor publish creates a new
numbered version; publishing to an existing named link can replace its
version in overwrite mode.
One command, any framework
Artor supports both working apps and static pages:
- Live apps can process forms, fetch data, and run other app features.
- Static sites display the pages and assets you publish, including plain HTML prototypes.
| Your project | How Artor serves it |
|---|---|
| Next.js | Live app (full Next.js) |
| React Router v7 (framework mode) | Live app |
| Remix | Live app |
| SvelteKit (Node adapter) | Live app |
| SvelteKit (static adapter) | Static site |
| TanStack Start | Live app |
| TanStack Router (SPA, no Start) | Static site |
| Angular | Static site |
| Vite | Static site |
| Astro | Static site |
| Create React App | Static site |
| Plain HTML (no framework) | Static site |
Next.js uses its live-app build by default. Use --static only if the prototype doesn’t
need features such as API routes or server rendering. React Router, Remix, and SvelteKit
Node builds need a live app and can’t be forced to static. An Angular app whose build also
produces a server bundle needs --static, which publishes its browser build only (see below).
TanStack Start needs its Node build output. If publishing can’t find it, give the error
to your coding agent to check the build target. Use --entry <path> for a custom location.
Plain TanStack Router projects without Start publish as static sites.
Angular apps publish as static sites. Artor recognizes an Angular app by an
angular.json file at the project root together with an Angular build package (such as
@angular/build). Angular used inside an Astro or Analog project doesn’t count: that project
keeps publishing as Astro or Analog. Artor reads the output folder from angular.json
(usually dist/<project>/browser), for the same project that the first ng build in your
build script selects (use --project in that script to be explicit), so you don’t need
--dir. If that path points outside your project, publishing stops before building and says so.
An Nx workspace has no angular.json, so pass --dir with your app’s browser output folder.
An Angular app that renders on the server (Angular SSR) stops before building. Artor doesn’t
run Angular’s server build, and a static publish would lose the server-rendered routes, so
Artor stops rather than drop them. Run artor publish --static to publish the browser
build only: the prerendered pages and the client app, without server routes. A build that
uses its server bundle only to prerender pages at build time stops the same way and asks for
--static; the message says the bundle is there for prerendering. An app built with
outputMode: "static" renders nothing on the server and needs no flag. If the browser build has
no index.html because the root route isn’t prerendered, publishing tells you; prerender the
root route (or turn server rendering off) and publish again.
Artor supports your project’s package manager, including pnpm; you don’t need to switch it to publish.
Monorepos: publish from the app’s folder
If your repository contains several apps, run Artor from the app you want to publish. Each app folder is linked to its own prototype:
cd apps/web
artor init # links this folder
artor publish # builds apps/web and ships its next versionThe project link is per-folder, so apps/web and apps/api are two independent prototypes,
each with its own versions and links.
Running artor publish at the root of a workspace usually won’t work: the root holds the
workspace setup, not an app, so publishing stops with
couldn't detect a framework, a build script, or an index.html. (or, when the root depends on a
framework, with a message that it has no build script). Change into the app’s folder and
publish from there.
A Next.js app inside a monorepo
A Next.js app in a pnpm, npm, Yarn or Bun workspace (with or without Turborepo) publishes with
plain artor publish from its own folder, as above. This requires artor-cli 0.34.0 or later
(check with artor --version). No extra flags are needed for the usual layouts. What Artor does
for you:
- Finds the server. In a monorepo, Next places its standalone server under
.next/standalone/<app path>/, for example.next/standalone/apps/web/server.js, not at the top of.next/standalone. Artor looks there, usingoutputFileTracingRootfrom yournext.configwhen it’s set, and runs the version from that path. - Puts the CSS, JS and
publicfiles next to the server, so the published page loads with its styles and scripts. - Installs missing dependencies at the workspace root. When your app’s folder has no lockfile
of its own and is a listed package of the workspace (
pnpm-workspace.yamlor theworkspacesfield in the rootpackage.json), Artor installs with the workspace’s package manager at the workspace root and builds the app with that same manager. The workspace’s package manager comes from its lockfile, then itspackageManagerfield, then pnpm if it has apnpm-workspace.yaml. Otherwise Artor installs in the app’s own folder.
If Artor can’t find the server, publishing stops and says why. When no server is found, the message lists every path it checked; when the search finds two servers, it names both; when the search had to stop early, it says so. These messages point you at one of two fixes:
- set
outputFileTracingRootinnext.configto your workspace root (also the fix when two apps build into the same standalone folder, or the folder is very deep or very large); or - publish the server yourself. After the build, copy
.next/staticto.next/standalone/<app path>/.next/staticandpublicto.next/standalone/<app path>/public(the folder that holdsserver.js), then runartor publish --node --dir .next/standalone --entry <app path>/server.js.
A server, or a .next, .next/static or public folder beside it, that is a symlink is refused
with the reason. Replace the symlink with a real file or folder.
Native modules. Artor doesn’t swap native modules such as sharp for their Linux builds in
a monorepo app; publishing prints a warning when the bundle carries sharp. If sharp must work
in the published app, publish from a Linux machine or CI.
Good to know:
- Artor reads
outputFileTracingRootonly when it’s written plainly innext.config(a string, orpath.join/path.resolveof__dirnameorimport.meta.dirname). A value computed any other way is ignored: Artor tries the path implied by your workspace, then a bounded search. - If npm fails on a
workspace:*dependency, check that the workspace’s package list includes your app’s folder. - Workspace membership follows pnpm’s
pnpm-workspace.yamlglobs and npm-styleworkspaces(used for Yarn and Bun too), excludes included. A workspace file Artor can’t read counts your app as listed, and a few pnpm edge cases differ between pnpm versions. - Artor decides whether dependencies are installed by looking in
node_modulesfolders from your app up to the workspace root. A dependency it can’t see there (one found throughNODE_PATHor above the workspace root, or a required dependency limited to another OS or CPU) reads as missing, so Artor installs again, or stops if you passed--no-install. - For npm, Yarn, Bun and hoisted pnpm workspace apps, Artor also checks dev dependencies. If a
production-only install (
npm install --omit=dev) leaves a declared dev dependency missing, Artor installs again before building. - Yarn Plug’n’Play has no
node_modules, so Artor reinstalls before every publish (Yarn makes this quick when nothing changed). - Without a Git repository above your app, Artor doesn’t look for a workspace in or above your home folder.
- A custom
distDirisn’t followed. Publish the server yourself: copy<distDir>/staticto<distDir>/standalone/<app path>/<distDir>/staticandpublicto<distDir>/standalone/<app path>/public, then runartor publish --node --dir <distDir>/standalone --entry <app path>/server.js. - A server folder whose name contains characters outside letters, digits,
.,_,-and/(a space, for example) can’t be published.
A plain HTML folder — no framework, no build
You don’t need a framework at all. If your project is just a hand-written index.html and
some assets — no framework, no build script, no build output folder — artor publish ships
the folder as a static site as-is, with no build step. Drop in an index.html, run
artor publish, and it’s live.
A couple of things to know:
- The homepage must be named
index.htmland sit at the project root. If there are.htmlfiles but none isindex.html, publishing stops with a clear message (found .html files but no index.html at the project root. Rename your entry page to index.html) rather than guessing which page is the homepage. - Artor never packs
node_modules,.git, build caches, or secret files into the published bundle, so a stray folder in your project won’t ride along. - This only kicks in when there’s no framework to detect — adding Next.js or Vite later means that version publishes as the right kind of app automatically, with no flags to remember.
Static prototypes get the review widget too. Artor adds commenting to the published
copy automatically, without changing your source files. If the widget is missing, check
that your HTML page includes a closing </body> tag and publish again.
Every publish is a fresh build
Each publish rebuilds your prototype from scratch to use your current code.
If you’ve just built and want to skip the rebuild, add --no-build to reuse what’s
already there (it fails clearly if there’s nothing built yet). Artor looks in your framework’s
own output folder first, such as dist for Vite. If more than one build folder is there (for
example both dist and out), Artor warns and names the one it packs. If your build writes to
a different folder, pass --dir <path> together with --no-build.
Artor installs the review widget automatically during setup. If required
packages are missing when you publish, it tries to install them first using your project’s
package manager. In a monorepo, a listed workspace app with no lockfile of its own installs at
the workspace root. Use --no-install only when those packages are already available, such as
on a machine that must stay offline; if they’re missing, publishing stops and names the folder
to run the install in (the workspace root for a listed app with no lockfile of its own;
otherwise, the app folder).
Broken builds fail before they go live
Artor checks your prototype before uploading it. If publishing stops:
- Missing homepage: make sure a static site’s main file is named
index.html. - Incomplete build: finish the build, or use
--dirto select the correct output folder. - App cannot start: give the error to your coding agent, fix the problem, and publish again.
If your app needs services or configuration that are only available on Artor, you can use
--no-smoke to skip the local startup check. Use this only when you know why the check fails.
Your existing versions remain available if a new publish fails before upload.
Artor confirms your link works
After publishing, Artor opens the new link once to check it responds. If you see a warning, open the link yourself and check it. The version is already live; this warning does not undo its publication. See Runtime errors if it cannot start.
If the connection drops during a publish
A version can go live even when its reply never reaches you: the Wi-Fi drops, a proxy cuts the connection, or the reply is cut short. With artor-cli 0.35.0 or later, a retried publish never creates a second version of the same build.
- One run, one publish. Each
artor publishrun tags its final step with its own key. If that step’s reply is lost, the CLI asks again with the same key (up to 2 more times, after a short pause). When the version already went live, Artor answers with that version instead of publishing it again, and the CLI prints its link as usual. If someone published a newer version after the lost attempt (solatestor your named link moved on), the link printed is that version’s own numbered link, never the newer version’s. With--json, the result addsreplayed: true. - It waits when Artor asks it to. While the first attempt is still being processed, or while your organization’s publish slot is busy or a short rate limit applies, the CLI waits and asks again, for up to about 5 minutes in total. A rate limit that would take longer than the time left (for example, your organization has used up its publishes for now) isn’t waited out: the publish stops at once with Artor’s message. When the time is up, the CLI stops and tells you to check the prototype’s versions in the dashboard in a few minutes.
- “This version may already be live.” Once an attempt has lost its reply, any failure later in
the same run says the version may already be live. Open the prototype in the dashboard and check
its versions before you publish again: a new
artor publishrun is a new publish, so if the version is already there, running it again creates another one. - Ctrl-C during the final step. If you stop the CLI while Artor is finishing the publish (or any time after an attempt lost its reply), it prints the same warning before exiting. Check the dashboard before publishing again.
- No automatic update after a lost attempt. If Artor asks for a newer CLI after an attempt
lost its reply, the CLI doesn’t update itself and re-run the publish (that re-run could create a
second version). It prints the warning, then asks you to run
artor updatebefore publishing again.
A retry only happens against an Artor server that supports it. An error Artor answered directly is final and is not retried. A few answers are final and publish nothing:
| Message | What it means |
|---|---|
| This publish already went live, but that version was changed or deleted afterwards. Nothing was republished. | Your publish worked, then someone changed or deleted that version before the retry arrived. |
| This publish did not go live, and that version was changed afterwards. Nothing was republished. | Your publish failed, and someone else changed that version since. |
| The server is still processing this publish. | The first attempt hasn’t finished after about 5 minutes of waiting. Check the prototype’s versions in a few minutes before publishing again. |
Scripts reading --json errors may also see the codes behind these situations:
publish_in_progress (the first attempt is still running; the CLI waits it out),
publish_conflict (a brief clash on Artor’s side; the CLI waits and retries),
publish_superseded and publish_failed_superseded (the two final answers above), and
publish_key_reused (the same key was sent with a different build, which the CLI never does).
How close you are to your limits
At the end of a publish, Artor shows a short usage summary, as bars that fill up: this
version’s build size and saved source size against their limits. Your organization’s current
storage and its public-link views each join the summary on their own, once that one reaches
75% of its limit. On an
interactive terminal the build and source bars always appear; in a script or CI log the
summary appears only once something reaches 75%. With --json, near-limit warnings are printed
as plain lines on the error output, so the result an agent reads stays clean.
Every publisher sees the build and source sizes. Storage and views show exact numbers to owners and admins; other members see a percentage. Anything near or over its limit comes with one next step: follow the one the CLI prints. Depending on your plan and role that is to upgrade in Settings > Billing, to add publisher seats in Settings > Team (on plans where seats add capacity), to contact support, or, for other members, to ask an org admin. When several limits share the same next step, it is printed once, after their lines. Upgrading is only suggested while self-serve upgrades are open. A publish is never failed by this summary; the limits themselves are listed in Plans & pricing.
Keeping the review widget current
If your project lists @artorapp/web-sdk (the in-page review widget) as
"latest", which is what artor init sets up, artor publish updates it to the newest version
whenever it builds your project. The update runs after dependencies are installed and before the
build, so the new widget is in the version you publish, and the saved source carries the
lockfile that built it. It never asks, and it works the same in a terminal, a script or CI.
Artor keeps "latest" in your package.json, in the same dependency list it was in, and
refreshes your lockfile, so a clean install afterwards still succeeds. Add --no-sdk-update to
skip it for one publish.
If you’ve pinned a version instead, such as ^0.9.0, publish leaves it alone and prints one line
suggesting "latest". A problem with the update (npm unreachable, a failed install) prints one
warning and never stops a publish: your package.json and lockfile go back to a matching pair.
If the new version installed but putting "latest" back or refreshing the lockfile failed, your
package.json keeps the version range your package manager wrote, and the warning asks you to set
it back to "latest", because publish leaves a pinned version alone from then on. A completed
update stays in place even if the build or upload fails afterwards.
Publish leaves the widget alone, and prints one line saying why, in three cases:
- You publish output you built yourself (
--dir,--node,--no-build, or a plain HTML folder published from its root). The widget is already inside it; the next publish that builds your project updates it. - Your prototype has no lockfile of its own inside a workspace (a monorepo where the lockfile
sits in a parent folder). Updating would change a lockfile shared with other folders, so the
line says where to update
@artorapp/web-sdk: from the workspace root when the workspace lists your prototype’s folder, otherwise in the prototype’s own folder. Keep the prototype’s"latest"and publish again. A prototype with its own lockfile is updated as usual. - Your project uses Yarn and its version can’t be read. Check that
yarn --versionworks.
Scripts and agents can read the outcome with --json; see
Scripting & agents.
A published version keeps the widget it was built with, so updating is how an existing prototype picks up widget improvements, such as the more reliable comment pins in widget version 0.10.0. Static HTML prototypes always get the current widget.
Notes for your coding agent
After every successful publish (and during artor init), Artor keeps a short, managed
“Review anchors (Artor)” section in your project’s AGENTS.md (or CLAUDE.md). It asks any
coding agent working in the folder to give the elements reviewers comment on a unique
data-testid, which keeps comment pins on the
right element. Nothing else in the file changes. Add --no-agent-notes to skip it for one run,
or see Review-anchor notes for coding agents
to turn it off for the folder.
Reconciling mock data before it ships
If local sample data differs from the version saved in Artor, publishing asks which copy to use. Choose local to keep your files or server to use the saved data.
For unattended publishing, use --mocks=local or --mocks=server when a choice is needed.
If Artor cannot check for differences, it also asks you to choose. See
Mock datasets for the full workflow.
Useful flags
Most publishes need no flags at all. The ones you might reach for:
| Flag | What it does |
|---|---|
--alias <name> | Publish to a named link, such as staging. May replace its existing version in overwrite mode. -v is the short form. |
-m "<note>" | Attach a changelog note describing what changed. |
--static | Force a static build (for an app you know is purely static). For an Angular app that renders on the server, publishes the browser build only. |
--no-build | Reuse the existing build instead of rebuilding. |
--mocks=local|server | Choose which sample data to keep when conflicts arise. Required for unattended runs with conflicts. |
--dir <path> | Publish an already-built folder as a static site, with no detection (or, with --node, as a server). |
--node | Publish an already-built Node server as-is, from --dir or the current folder. No build, no detection. |
--entry <path> | With --node, the server file to start (default server.js): a relative .js, .mjs or .cjs path inside the folder. |
--no-sdk-update | Don’t update the review widget SDK on this publish. |
--no-agent-notes | Don’t add or refresh the review-anchor notes in AGENTS.md / CLAUDE.md this time. |
--list-source | Print every file the saved source would include, with sizes, then exit. No build, no upload. |
--yes | Skip confirmation prompts — needed when running unattended or in CI. |
--json | Print a result an agent or script can read. |
--version <name> is still accepted as an older spelling of --alias, but it prints a
deprecation notice: everywhere else in the CLI --version means a version number
(artor open --version 3). Use --alias.
--static only applies to static-capable projects. Asking Artor to force a static build of
an app that needs a backend (React Router, Remix, SvelteKit Node) fails with a clear
message rather than shipping a broken site. Angular with server rendering is the one case
where --static is the way forward: it publishes the browser build, without server routes.
What goes into the saved source
Every publish also saves a copy of your project files, so you or a teammate can get the code back or remix it later. Artor is a preview tool, not source control, so the copy is meant to be small. Its file list follows three rules, in this order:
- Your
.gitignorefiles are honoured, at every level of the project and in the parent folders up to the repository root. A nested app’s own.gitignorecounts too, so anios/,android/,dist/or.expo/folder that git ignores is never uploaded, and publishing from one app of a monorepo still applies the root rules. You do not need git installed for this: Artor reads the files itself. - An optional
.artorignoreuses the same syntax and applies on top. Use it to leave out files that git tracks but that should not be shared (large fixtures, design sources), or to force-include a gitignored file with a!line, for example!generated/schema.jsonwhen a remix needs it. It works in a project with no git at all. Only.artorignorefiles inside the folder you publish are read, not one in a parent folder. - Secrets and local-only folders are always left out, and no ignore file can put them
back, such as
.env*,.npmrc,.git-credentials,.pgpass,.dev.vars, key and certificate files,node_modules,.git,.next, the.artorlink file, and cloud or tool config folders like.aws,.ssh,.dockerand.vercel.
Folders that are ignored are skipped entirely, so a multi-gigabyte native build folder
costs nothing at publish time. A file inside an ignored folder cannot be restored on its own
(the same rule git uses): restore the folder first with !dist/, then add narrower excludes.
.git/info/exclude and your global git excludes are not read; put those rules in
.artorignore if they matter. Symlinks are skipped with a warning when they point outside the
project, at an excluded or ignored file, or loop.
A hand-written static site published from the project root (no build step) is served from
the same files, so there only .artorignore and the secret excludes shape the served site,
never .gitignore: a gitignored generated output.css still ships. For such a site,
.artorignore removes a file from the served site too, so use .gitignore for large files the
page still needs (a remix will then lack them; if it needs them, make them smaller instead). .gitignore and .artorignore themselves are never served.
If the rules leave nothing to save (for example a parent folder’s .gitignore that contains
*), the version still ships, and Artor warns you, naming the file responsible: there will be
no code for a remix to restore. To keep your files, add ! lines for them to your project’s
own .artorignore.
The saved source is capped at 50 MB compressed. Artor measures it before building or
uploading anything (the CLI prints Checking source snapshot… while it does) and stops with the
total and the heaviest folders and files when it is over. It also stops if the files would
unpack past 200 MB of file content or 100,000 files, or compress unusually well (over 15 times).
When the file sizes alone already break the 200 MB or 100,000-file limit, it stops without
reading or compressing any file, with the same report. From 75% of the cap (about 37.5 MB) it
prints a warning. There is no override: add an ignore rule, then run artor publish --list-source to see exactly what would ship (--json for scripts,
including whether a publish would stop and why; it needs no sign-in).
Before artor-cli 0.28.0 the saved source ignored your .gitignore, so a gitignored file
that a remix used to restore is no longer included. Add it back with a ! line in
.artorignore.
Honest limits
- A prototype can read any value you give it. Treat previews as staging, and use staging credentials in your environment variables, never production ones.
- Live apps take a moment to wake. The first open after a while may be a little slow while the app starts; it stays warm after that.
- A server crash in the middle of a publish. If the server crashes in the middle of a publish, just publish again: the new publish works. The crashed attempt can be left behind as a version stuck in progress (never served) until automatic cleanup lands. Until then, deleting that version reports it as busy. If the crashed publish was replacing a named link’s version (overwrite mode), that version is unavailable until it is published over again.
- A retry only covers the same run. Artor recognizes a repeated publish only within one
artor publishrun. After you stop the CLI or it exits, check the prototype’s versions before publishing again. See If the connection drops during a publish. - Build limits depend on your plan. Static builds can be up to 50 MB on Starter,
100 MB on Pro, and 200 MB on Team and Enterprise. Live apps can be up to 200 MB on
Starter, 500 MB on Pro, 1,000 MB on Team, and 1,024 MB on Enterprise. Your
organization may have a custom limit. See Plans & pricing.
If a build is too big, make it smaller: for a static site, large media is the usual cause; for
a live app, dependencies are usually most of it, so keep build-only packages in
devDependencies. Extra publisher seats never raise the build limit. The refusal starts with “Build too large:” and names the static build limit or the live app build limit that applies, then the one next step for your organization, and that step is the one to follow. It offers an upgrade only when a higher plan really has more room for your organization and self-serve upgrades are open, so a custom limit or paused upgrades can rule it out on any plan; otherwise the step is to contact support. Members who are not an owner or admin are told to ask one. When an upgrade would help, the CLI adds “See plan limits: https://artor.app/#pricing ”. - Saved source has a separate limit. Its compressed upload can be up to 50 MB, checked locally before anything is sent. Large videos, datasets, and design exports can make the source too large even when the app itself is small; see What goes into the saved source.
- Saved source leaves out ignored files, dependency folders, and recognized credential files. Check your project before publishing: a password written into an ordinary source file or included in a built page can still be shared. See Getting the source back.
Related
- The publish → review loop — the full round-trip with your team
- Deployments & versions — how version links and aliases work
- CLI reference — every command and flag
- Getting the source back — pull a version’s code or fork it