Deploy HTML prototypes to Google Cloud Storage and share URLs with the team.
- Node.js 18+
- Google Cloud CLI (
gcloud) - A GCP project with the Cloud Storage API enabled
1. Create a service account and key
gcloud iam service-accounts create mockups-publisher \
--display-name="Mockups publisher"
gcloud iam service-accounts keys create ./service-account.json \
--iam-account="mockups-publisher@$(gcloud config get-value project).iam.gserviceaccount.com"service-account.json is gitignored — keep it out of version control.
2. Install dependencies
npm install3. Configure environment
cp .env.example .envEdit .env and fill in:
| Variable | Description |
|---|---|
GCP_PROJECT_ID |
Your GCP project ID (gcloud config get-value project) |
GCS_BUCKET_NAME |
A globally unique bucket name, e.g. prototypes-yourcompany |
GCS_REGION |
(optional) Storage region, defaults to us-central1 |
GCP_KEY_FILE |
Path to the service account key, e.g. ./service-account.json |
The bucket is created automatically on first deploy with public read access, and the service account is granted roles/storage.admin on it.
If you'd rather use your own personal credentials instead of a service account, omit
GCP_KEY_FILEand rungcloud auth application-default login— the script falls back to Application Default Credentials.
Note: If your GCP organization enforces "Public Access Prevention," you'll need to disable it for the project before deploying: GCP Console → Cloud Storage → Settings → Public access prevention.
Place the prototype in its own folder under prototypes/:
prototypes/
my-feature/
index.html ← served as-is
another-feature/
Wireframe.html ← single HTML file, auto-renamed to index.html
Wireframe_files/ ← assets folder uploaded alongside
full-app/
package.json ← detected as a package; build runs before upload
src/
dist/ ← uploaded after build
Prototype types are detected automatically:
- Single HTML file (with optional asset folders) — uploaded as-is; a lone non-
index.htmlfile is renamed toindex.htmlso the URL is clean. - Full package — if
package.jsonis present,npm install && npm run buildruns first and thedist/(orbuild/) folder is uploaded.
To pull in prototypes stored outside this project (e.g. Claude project folders), create a mockups.config.json at the project root:
{
"sources": [
{
"path": "/Users/you/Documents/Claude/Projects/My Feature/prototype",
"slug": "my-feature"
},
"/absolute/path/to/a-folder-of-mockups"
]
}Each entry can be a plain path string or an object with path and an optional slug. The path is auto-detected:
- Single file — a path pointing directly at a file (e.g. a single
.htmlmockup) is uploaded as its own prototype. Theslug(or the filename without its extension, if omitted) becomes the GCS prefix. A lone HTML file is renamed toindex.htmlon upload, same as the single-HTML-file case below. - Single prototype — if the folder itself contains
.htmlfiles or apackage.json, it's treated as one prototype. Theslug(or the folder name if omitted) becomes the GCS prefix. - Container of prototypes — otherwise, each subdirectory is treated as an individual prototype, exactly like
prototypes/.slugis not applicable here.
Paths starting with ~/ are expanded to your home directory. file:// URLs (including %20-style escapes, as produced by e.g. Claude's file picker) are also accepted and resolved to a local path. The built-in prototypes/ folder is always included first.
mockups.config.json is gitignored since paths are machine-specific.
If two sources produce a prototype with the same name, the first one wins and a warning is printed.
npm run deployThe script syncs all source folders to GCS and prints URLs for anything that changed:
project-1... no changes
project-2... updated
Updated:
project-2: https://storage.googleapis.com/your-bucket/project-2/index.html
Delete the folder from its source directory and run npm run deploy. The script detects that the prefix no longer exists locally and removes it from the bucket.
- Files are compared by MD5 checksum — only changed files are uploaded.
- Orphaned files within a prototype (renamed or deleted locally) are removed from the bucket.
- Prototype folders deleted locally are fully removed from the bucket on the next deploy.
- The bucket is created once with uniform public read access; subsequent deploys only touch objects.