Build an app to publish
What your app has to be before Panoply can host it
Panoply hosts what you upload. That means your app has to build to static output — HTML, CSS, and JavaScript that a browser can run without a server behind it.
Packaging your app
- A single
.htmlfile: published as-is, no build step. - Any project that builds to a static site: if
npm run buildproduces static HTML, CSS and JavaScript, it works. Most commonly React, Vue or Svelte with Vite.
- Apps that bring their own server: server-side rendering (Next.js, Nuxt), API routes, and self-hosted databases. Static output only.
package.jsonwith a build script at the root of the archive- no
.envfiles node_modules,.git, macOS files and prebuiltdist/buildoutput are stripped automatically: leave them in or take them out, either works- under 50MB unzipped (measured after the strip above)
- Zip your project folder, with
package.jsonat the root of the archive. Panoply runs the build for you, so there's no need to build first:zip -r app.zip .(run from your project root). Uploadapp.zipthrough the publish form's file picker.
There are two shapes that work, and a zip is not a way to ship a folder of static files — it's how you ship a project we build for you.
Option 1: a single HTML file
Simplest path. Inline your CSS and JavaScript into one file and upload it. No build step, nothing to configure. Good for self-contained tools, toys, and anything you built in one file to begin with.
Option 2: a zipped project
Zip the contents of your project folder, so package.json sits at the top level of the archive rather than one directory down. (If your tool wraps everything in a single folder — macOS right-click → Compress does this — we unwrap it for you.)
A zip without a package.json and a build script is rejected at scan. If your app is already plain static files, upload the HTML file itself rather than zipping the folder.
Frameworks
There is no list of approved frameworks. We run your build script and check what comes out: if it's a static site, it works; if it needs a server, it doesn't.
| What you're building | Works |
|---|---|
| Anything whose build emits static HTML / CSS / JS | Yes |
| React, Vue or Svelte on Vite — the common case | Yes |
| A framework we've never seen, with a build script | Yes, if the output is static |
| SvelteKit | Yes, with adapter-static |
| Next.js, Nuxt, or anything server-rendered | No |
| Your own backend server or self-hosted database | No — but you don't need one for per-user data; see below |
Common build commands produce a dist/ or build/ directory — that's what Panoply serves.
Storing data
"No server" doesn't mean "no saved data". Every deployed app can save per-user data through the ambient window.panoplyStorage API, so the usual reason to stand up a backend — remembering what someone did — is already handled.
How it's backed depends on whether the app holds a storage slot: apps with a slot get server-side storage scoped to each signed-in user and encrypted at rest; apps without one get the same API backed by the visitor's browser. Your code is identical either way. It is not a secrets vault — don't put API keys or credentials in it.
See App storage for the API, the two backings, and the limits.
Before you upload
- Test the built output, not the dev server. If it only works under
npm run dev, it won't work hosted. - Check every path is relative. Absolute paths to your machine break once deployed.
- Make sure it does nothing review will reject — see What review checks for.