npx claudepluginhub cwinvestments/memstack --plugin memstackThis skill uses the workspace's default tool permissions.
*Validates build config, redirects, environment variables, and deployment readiness for Netlify static/SPA hosting.*
Deploys web projects to Netlify using Netlify CLI. Handles authentication, site linking or creation via Git remote, and production/preview deployments.
Deploys web projects to Netlify using Netlify CLI. Manages authentication, project detection, site linking/creation, and production/preview deploys on deploy/host/publish requests.
Deploys web projects to Netlify using Netlify CLI. Automates authentication verification, site linking via Git, creation, and production/preview deploys.
Share bugs, ideas, or general feedback.
Validates build config, redirects, environment variables, and deployment readiness for Netlify static/SPA hosting.
When this skill activates, output:
π Netlify Deploy β Running pre-flight checks...
Then execute the protocol below.
| Context | Status |
|---|---|
| User says "deploy to netlify" or "netlify deploy" | ACTIVE |
| User says "deploy frontend" or "deploy static site" | ACTIVE |
| Preparing a React/Vue/Svelte/Next.js static export for hosting | ACTIVE |
| Deploying a backend service or API server | DORMANT β use railway-deploy |
| Discussing Netlify pricing or features generally | DORMANT |
| Trap | Reality Check |
|---|---|
| "I'll configure it in the Netlify UI" | netlify.toml is version-controlled and portable. UI settings get lost across teams. |
| "Redirects work fine without _redirects" | SPA routing breaks on refresh without /* /index.html 200. Every SPA needs this. |
| "Environment vars are the same everywhere" | Build-time vars (baked into JS bundle) vs runtime vars (Netlify Functions) are different. NEXT_PUBLIC_ prefix exposes to client. |
| "The API proxy just works" | /api/* redirects must point to your actual backend URL. Forgetting to update after backend redeploy breaks the frontend. |
| "I'll check the deploy after pushing" | netlify deploy --build locally catches build failures before they hit production. Always build locally first. |
Look for Netlify config and verify build settings:
# Check for Netlify configuration
ls netlify.toml _redirects _headers 2>/dev/null
# Check package.json for build script
cat package.json | grep -A2 '"scripts"' | grep '"build"'
If netlify.toml exists, verify it:
# Expected structure
[build]
command = "npm run build" # or "yarn build", "pnpm build"
publish = "dist" # or "build", "out", ".next" (varies by framework)
[build.environment]
NODE_VERSION = "20" # Pin Node version for reproducible builds
| Framework | Build Command | Publish Directory |
|---|---|---|
| React (CRA) | npm run build | build |
| React (Vite) | npm run build | dist |
| Next.js (static) | next build && next export | out |
| Vue | npm run build | dist |
| Svelte/SvelteKit | npm run build | build |
| Astro | npm run build | dist |
| Plain HTML | β | . or public |
Flag if: netlify.toml missing or publish directory doesn't match framework default.
# Check redirect files
cat netlify.toml 2>/dev/null | grep -A5 '\[\[redirects\]\]'
cat _redirects 2>/dev/null
Check for the API proxy pattern (frontend β backend):
# netlify.toml β API proxy to Railway/external backend
[[redirects]]
from = "/api/*"
to = "https://your-backend.up.railway.app/api/:splat"
status = 200
force = true
Or in _redirects:
/api/* https://your-backend.up.railway.app/api/:splat 200
Verify:
status = 200 (proxy, not redirect β preserves the URL for the client)force = true if the proxy should override static files at the same pathlocalhost:3000 β update to productionFlag if: Code references /api/ paths but no proxy redirect is configured.
Single-page apps need a catch-all redirect so deep links and page refreshes work:
# Check for SPA redirect
grep -r "\/\*.*\/index\.html\|\/\*.*200" netlify.toml _redirects 2>/dev/null
Required for SPAs (React Router, Vue Router, etc.):
# In _redirects (must be LAST rule β order matters)
/* /index.html 200
Or in netlify.toml:
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
Flag if: Project uses client-side routing but no catch-all redirect exists. Symptoms: pages work when navigated to via links, but return 404 on direct URL access or refresh.
Note: Next.js static export handles this differently β each page is pre-rendered as its own HTML file. SPA redirect is NOT needed for static Next.js.
# Find env vars used in frontend code
grep -rn "process\.env\.\|import\.meta\.env\.\|VITE_\|NEXT_PUBLIC_\|REACT_APP_" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" . | grep -v node_modules
Build-time vs runtime separation:
| Prefix | Framework | When Available | Exposed to Client? |
|---|---|---|---|
REACT_APP_ | CRA | Build time | β οΈ YES β baked into JS bundle |
NEXT_PUBLIC_ | Next.js | Build time | β οΈ YES β baked into JS bundle |
VITE_ | Vite | Build time | β οΈ YES β baked into JS bundle |
| No prefix | Any | Build time only | β No β server-side/build scripts only |
Critical security check:
# Search for secrets that might be exposed to client
grep -rn "NEXT_PUBLIC_.*SECRET\|NEXT_PUBLIC_.*KEY\|VITE_.*SECRET\|REACT_APP_.*SECRET" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.env*" . | grep -v node_modules
Flag if: Any secret (API keys with write access, database URLs, auth secrets) uses a client-exposed prefix. These are baked into the JavaScript bundle and visible to anyone who opens DevTools.
Output: List each variable with where to set it:
netlify.toml [build.environment] (for non-sensitive build config like NODE_VERSION)# Check for domain configuration
cat netlify.toml 2>/dev/null | grep -A5 '\[context\]'
Verify in Netlify dashboard:
*.netlify.app or A record to Netlify load balancer)Flag if: Domain is added but DNS hasn't propagated or SSL shows "Waiting for DNS verification."
# Check for serverless functions
ls netlify/functions/ functions/ 2>/dev/null
cat netlify.toml 2>/dev/null | grep 'functions'
If functions exist, verify:
netlify.toml: [functions] directory = "netlify/functions"export const handler = async (event, context) => { ... }# Check for security headers
cat netlify.toml 2>/dev/null | grep -A10 '\[\[headers\]\]'
cat _headers 2>/dev/null
Recommended security headers:
# netlify.toml
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
Permissions-Policy = "camera=(), microphone=(), geolocation=()"
Build locally to catch errors before Netlify builds:
# Local build test
npm run build
# Check output directory exists and has content
ls -la dist/ # or build/, out/, etc.
# Check for common issues
grep -rn "http://localhost\|http://127\.0\.0\.1" dist/ 2>/dev/null
| Check | Command | Pass Criteria |
|---|---|---|
| Build passes | npm run build | Exit code 0, no errors |
| Output directory exists | ls dist/ | Has index.html and assets |
| No localhost in build | grep dist/ for localhost | Zero matches |
| _redirects in output | ls dist/_redirects | Exists if using _redirects approach |
| Env vars documented | Check .env.example | All client vars listed |
| No secrets in client code | grep for exposed secrets | Zero matches |
| Git clean | git status | All changes committed |
Output pre-deploy summary:
π Netlify Deploy β Pre-flight Complete
Project: [name] ([framework])
Build: β
passes β [publish directory]
Redirects: β
SPA routing + API proxy configured
Env vars: β
8 build-time vars, no secrets exposed
Domain: β
[domain] with SSL
Functions: β
2 functions in netlify/functions/
Headers: β
security headers configured
Ready to deploy.
Preview: netlify deploy --build
Production: netlify deploy --build --prod
After deployment completes:
/dashboard/settings) β should load, not 404https://[domain] β padlock should appear, no mixed content warningshttp://[domain] β should redirect to https://Rollback plan: