Skip to contentSkip to Content
Publishing & frameworks

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 below

You 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 projectHow Artor serves it
Next.jsLive app (full Next.js)
React Router v7 (framework mode)Live app
RemixLive app
SvelteKit (Node adapter)Live app
SvelteKit (static adapter)Static site
TanStack StartLive app
TanStack Router (SPA, no Start)Static site
AngularStatic site
ViteStatic site
AstroStatic site
Create React AppStatic 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 version

The 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, using outputFileTracingRoot from your next.config when it’s set, and runs the version from that path.
  • Puts the CSS, JS and public files 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.yaml or the workspaces field in the root package.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 its packageManager field, then pnpm if it has a pnpm-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 outputFileTracingRoot in next.config to 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/static to .next/standalone/<app path>/.next/static and public to .next/standalone/<app path>/public (the folder that holds server.js), then run artor 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 outputFileTracingRoot only when it’s written plainly in next.config (a string, or path.join / path.resolve of __dirname or import.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.yaml globs and npm-style workspaces (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_modules folders from your app up to the workspace root. A dependency it can’t see there (one found through NODE_PATH or 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 distDir isn’t followed. Publish the server yourself: copy <distDir>/static to <distDir>/standalone/<app path>/<distDir>/static and public to <distDir>/standalone/<app path>/public, then run artor 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.html and sit at the project root. If there are .html files but none is index.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 --dir to 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.

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 publish run 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 (so latest or your named link moved on), the link printed is that version’s own numbered link, never the newer version’s. With --json, the result adds replayed: 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 publish run 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 update before 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:

MessageWhat 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 --version works.

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:

FlagWhat 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.
--staticForce 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-buildReuse the existing build instead of rebuilding.
--mocks=local|serverChoose 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).
--nodePublish 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-updateDon’t update the review widget SDK on this publish.
--no-agent-notesDon’t add or refresh the review-anchor notes in AGENTS.md / CLAUDE.md this time.
--list-sourcePrint every file the saved source would include, with sizes, then exit. No build, no upload.
--yesSkip confirmation prompts — needed when running unattended or in CI.
--jsonPrint 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:

  1. Your .gitignore files are honoured, at every level of the project and in the parent folders up to the repository root. A nested app’s own .gitignore counts too, so an ios/, 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.
  2. An optional .artorignore uses 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.json when a remix needs it. It works in a project with no git at all. Only .artorignore files inside the folder you publish are read, not one in a parent folder.
  3. 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 .artor link file, and cloud or tool config folders like .aws, .ssh, .docker and .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 publish run. 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.