/deploy-frontend
Use when deploying a frontend (React, Next.js, or static HTML) to a live URL on Butterbase, or when troubleshooting deployment issues like MIME type errors or blank pages
$ npx -y skills add butterbase-ai/butterbase-skills --skill deploy-frontend --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/deploy-frontend
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use when deploying a frontend (React, Next.js, or static HTML) to a live URL on Butterbase, or when troubleshooting deployment issues like MIME type errors or blank pages
SKILL.md
deploy-frontend.SKILL.mdname: deploy-frontend
description: Use when deploying a frontend (React, Next.js, or static HTML) to a live URL on Butterbase, or when troubleshooting deployment issues like MIME type errors or blank pages
Overview
7-step workflow for deploying static frontends to Butterbase. Covers building, CORS, zipping, uploading, and verification.
---
Framework Reference Table
| Framework | Build command | Output dir | Env prefix | Framework flag | |-----------------|-----------------|--------------|-----------------|-----------------| | React (Vite) | `npm run build` | `dist/` | `VITE_` | `react-vite` | | Next.js (static)| `next build` | `out/` | `NEXT_PUBLIC_` | `nextjs-static` | | Plain HTML | (none) | project root | N/A | `static` |
> **Note:** Next.js requires `output: 'export'` in `next.config.js` to produce a static export.
---
Step 1: Set Environment Variables
Use `manage_frontend` with `action: "set_env"` to configure the API URL and app ID before building. These variables are injected at build time by the framework.
{
"app_id": "app_abc123",
"action": "set_env",
"vars": {
"VITE_API_URL": "https://api.butterbase.ai/v1/app_abc123",
"VITE_APP_ID": "app_abc123"
}
}- For Vite, prefix all public variables with `VITE_`
- For Next.js, prefix with `NEXT_PUBLIC_`
- For Create React App, prefix with `REACT_APP_`
`set_env` upserts; you can call it again to add or change variables.
---
Step 2: Build
Run the framework-specific build command to produce the static output directory.
| Framework | Command | |------------------|-------------------| | React (Vite) | `npm run build` | | Next.js (static) | `next build` | | Plain HTML | (no build needed) |
After building, verify the output directory contains `index.html` at its root:
# For Vite
ls dist/index.html
# For Next.js static export
ls out/index.html
If `index.html` is missing, check that the build completed without errors and that the framework is configured for static output.
---
Step 3: Configure CORS
Before deploying, configure CORS so the browser can make API requests from the deployment URL.
Call `manage_app` with `action: "update_cors"`. Pass the deployment URL (use the Butterbase Pages URL pattern) and any local dev origins:
{
"app_id": "app_abc123",
"action": "update_cors",
"allowed_origins": [
"https://your-app.pages.dev",
"http://localhost:5173"
]
}- Always include `http://localhost:5173` (Vite dev server default) for local development
- Include `http://localhost:3000` if using Next.js or Create React App locally
- Origins must include the protocol (`https://` or `http://`) and must not have trailing slashes
- If you don't yet know the exact deployment URL, you can update CORS again after Step 7
---
Step 4: Create Deployment
Call `create_frontend_deployment` with the `app_id` and the correct `framework` flag from the reference table above.
{
"app_id": "app_abc123",
"framework": "react-vite"
}The response contains:
- `deployment_id` — save this for Step 7
- `uploadUrl` — the presigned S3 URL for uploading the zip (expires in 15 minutes)
> **Free plan:** 1 deployment per app. Deploying again automatically replaces the previous deployment — no need to delete first.
---
Step 5: Create Zip (Node `archiver` — the only supported method)
> ⚠️ **Do not use `Compress-Archive`, File Explorer, or `zip -r` from outside the build dir.** Windows built-in tools write backslash (`\`) path separators, which makes the platform serve every file as `text/html` and breaks JS/CSS with MIME errors. Zipping from the parent dir nests `dist/` inside the archive and ships a blank page.
Butterbase's recommended cross-platform method is the [`archiver`](https://www.npmjs.com/package/archiver) Node package. It always writes POSIX `/` separators (works identically on macOS, Linux, Windows PowerShell, cmd, Git Bash, WSL) and zips from *inside* the source dir so `index.html` lands at the zip root.
**One-time setup in the project being deployed:**
npm install --save-dev archiver
mkdir -p scripts
**Then save this as `scripts/make-zip.mjs` (copy verbatim):**
#!/usr/bin/env node
/**
* Butterbase frontend zipper — the only supported way to compress a build
* for `create_frontend_deployment` / `create_from_source`.
*
* Usage:
* node scripts/make-zip.mjs <sourceDir> <outZip> [--exclude=glob,glob,...]
*
* Examples:
* node scripts/make-zip.mjs dist frontend.zip # Vite
* node scripts/make-zip.mjs out frontend.zip # Next.js static export
* node scripts/make-zip.mjs . source.zip \ # source-build flow
* --exclude=node_modules,.next,dist,out,.git,.turbo,.cache
*/
import { createWriteStream } from "node:fs";
import { stat } from "node:fs/promises";
import { resolve } from "node:path";
import archiver from "archiver";
const [, , srcArg, outArg, ...rest] = process.argv;
if (!srcArg || !outArg) {
console.error(
"usage: node make-zip.mjs <sourceDir> <outZip> [--exclude=glob,glob,...]"
);
process.exit(2);
}
const src = resolve(srcArg);
const out = resolve(outArg);
const excludeFlag = rest.find((a) => a.startsWith("--exclude="));
const excludes = excludeFlag
? excludeFlag
.slice("--exclude=".length)
.split(",")
.map((s) => s.trim())
.filter(Boolean)
.flatMap((g) => [g, `${g}/**`])
: [];
const srcStat = await stat(src).catch(() => null);
if (!srcStat?.isDirectory()) {
console.error(`error: source is not a directory: ${src}`);
process.exit(1);
}
const output = createWriteStream(out);
const archive = archiver("zip", { zlib: { level: 9 }, forceLocalTime: true });
output.on("close", () => {
const mb = (archive.pointer() / (1024 * 1024)).toFixed(2);
console.log(`wrote ${out} (${mb} MB, ${archivRead more
name: deploy-frontend description: Use when deploying a frontend (React, Next.js, or static HTML) to a live URL on Butterbase, or when troubleshooting deployment issues like MIME type errors or blank pages
Overview
7-step workflow for deploying static frontends to Butterbase. Covers building, CORS, zipping, uploading, and verification.
---
Framework Reference Table
| Framework | Build command | Output dir | Env prefix | Framework flag | |-----------------|-----------------|--------------|-----------------|-----------------| | React (Vite) | `npm run build` | `dist/` | `VITE_` | `react-vite` | | Next.js (static)| `next build` | `out/` | `NEXT_PUBLIC_` | `nextjs-static` | | Plain HTML | (none) | project root | N/A | `static` |
> **Note:** Next.js requires `output: 'export'` in `next.config.js` to produce a static export.
---
Step 1: Set Environment Variables
Use `manage_frontend` with `action: "set_env"` to configure the API URL and app ID before building. These variables are injected at build time by the framework.
{
"app_id": "app_abc123",
"action": "set_env",
"vars": {
"VITE_API_URL": "https://api.butterbase.ai/v1/app_abc123",
"VITE_APP_ID": "app_abc123"
}
}- For Vite, prefix all public variables with `VITE_`
- For Next.js, prefix with `NEXT_PUBLIC_`
- For Create React App, prefix with `REACT_APP_`
`set_env` upserts; you can call it again to add or change variables.
---
Step 2: Build
Run the framework-specific build command to produce the static output directory.
| Framework | Command | |------------------|-------------------| | React (Vite) | `npm run build` | | Next.js (static) | `next build` | | Plain HTML | (no build needed) |
After building, verify the output directory contains `index.html` at its root:
# For Vite ls dist/index.html # For Next.js static export ls out/index.html
If `index.html` is missing, check that the build completed without errors and that the framework is configured for static output.
---
Step 3: Configure CORS
Before deploying, configure CORS so the browser can make API requests from the deployment URL.
Call `manage_app` with `action: "update_cors"`. Pass the deployment URL (use the Butterbase Pages URL pattern) and any local dev origins:
{
"app_id": "app_abc123",
"action": "update_cors",
"allowed_origins": [
"https://your-app.pages.dev",
"http://localhost:5173"
]
}- Always include `http://localhost:5173` (Vite dev server default) for local development
- Include `http://localhost:3000` if using Next.js or Create React App locally
- Origins must include the protocol (`https://` or `http://`) and must not have trailing slashes
- If you don't yet know the exact deployment URL, you can update CORS again after Step 7
---
Step 4: Create Deployment
Call `create_frontend_deployment` with the `app_id` and the correct `framework` flag from the reference table above.
{
"app_id": "app_abc123",
"framework": "react-vite"
}The response contains:
- `deployment_id` — save this for Step 7
- `uploadUrl` — the presigned S3 URL for uploading the zip (expires in 15 minutes)
> **Free plan:** 1 deployment per app. Deploying again automatically replaces the previous deployment — no need to delete first.
---
Step 5: Create Zip (Node `archiver` — the only supported method)
> ⚠️ **Do not use `Compress-Archive`, File Explorer, or `zip -r` from outside the build dir.** Windows built-in tools write backslash (`\`) path separators, which makes the platform serve every file as `text/html` and breaks JS/CSS with MIME errors. Zipping from the parent dir nests `dist/` inside the archive and ships a blank page.
Butterbase's recommended cross-platform method is the [`archiver`](https://www.npmjs.com/package/archiver) Node package. It always writes POSIX `/` separators (works identically on macOS, Linux, Windows PowerShell, cmd, Git Bash, WSL) and zips from *inside* the source dir so `index.html` lands at the zip root.
**One-time setup in the project being deployed:**
npm install --save-dev archiver mkdir -p scripts
**Then save this as `scripts/make-zip.mjs` (copy verbatim):**
#!/usr/bin/env node
/**
* Butterbase frontend zipper — the only supported way to compress a build
* for `create_frontend_deployment` / `create_from_source`.
*
* Usage:
* node scripts/make-zip.mjs <sourceDir> <outZip> [--exclude=glob,glob,...]
*
* Examples:
* node scripts/make-zip.mjs dist frontend.zip # Vite
* node scripts/make-zip.mjs out frontend.zip # Next.js static export
* node scripts/make-zip.mjs . source.zip \ # source-build flow
* --exclude=node_modules,.next,dist,out,.git,.turbo,.cache
*/
import { createWriteStream } from "node:fs";
import { stat } from "node:fs/promises";
import { resolve } from "node:path";
import archiver from "archiver";
const [, , srcArg, outArg, ...rest] = process.argv;
if (!srcArg || !outArg) {
console.error(
"usage: node make-zip.mjs <sourceDir> <outZip> [--exclude=glob,glob,...]"
);
process.exit(2);
}
const src = resolve(srcArg);
const out = resolve(outArg);
const excludeFlag = rest.find((a) => a.startsWith("--exclude="));
const excludes = excludeFlag
? excludeFlag
.slice("--exclude=".length)
.split(",")
.map((s) => s.trim())
.filter(Boolean)
.flatMap((g) => [g, `${g}/**`])
: [];
const srcStat = await stat(src).catch(() => null);
if (!srcStat?.isDirectory()) {
console.error(`error: source is not a directory: ${src}`);
process.exit(1);
}
const output = createWriteStream(out);
const archive = archiver("zip", { zlib: { level: 9 }, forceLocalTime: true });
output.on("close", () => {
const mb = (archive.pointer() / (1024 * 1024)).toFixed(2);
console.log(`wrote ${out} (${mb} MB, ${archivClaude Code plugin for Butterbase — the AI-Native Backend-as-a-Service. This plugin gives Claude deep knowledge of Butterbase's 42+ MCP tools, guides you through common workflows, and auto-configures the MCP server connection.
Repo: butterbase-ai/butterbase-skills
Other skills on butterbase-skills.
- /agents
Use when designing, deploying, or debugging a Butterbase Agent (declarative LLM/tool graph), registering an MCP server for tool use, or wiring access controls and rate limits. Agents are first-class app resources defined by a `graph_spec` and invoked over
Open skill - /ai
Use when calling the app's AI gateway from agent tools — chat completions, embeddings, listing models, configuring defaults or BYOK, reading token/cost usage
Open skill - /auth-setup
Use when configuring OAuth providers (Google/GitHub/Apple/X/etc.), setting up post-login auth hooks, tuning JWT lifetimes, or generating service API keys
Open skill - /build-app
Use when building a new Butterbase app from scratch, creating a full-stack application, or when the user asks to set up a complete backend with database, auth, and deployment
Open skill - /contributing
Use when contributing to the Butterbase codebase, adding new MCP tools, creating API routes, writing migrations, or understanding the monorepo architecture
Open skill - /debug-rls
Use when users report access denied errors, see wrong data, RLS policies are not working, or when troubleshooting Row-Level Security issues in Butterbase
Open skill

