Marketplace web-build-deploy
Building and deploying React web applications. Use when configuring builds, deploying to Vercel/Netlify, setting up CI/CD, Docker, or managing environments.
install
source · Clone the upstream repo
git clone https://github.com/aiskillstore/marketplace
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/cjharmath/web-build-deploy" ~/.claude/skills/aiskillstore-marketplace-web-build-deploy && rm -rf "$T"
manifest:
skills/cjharmath/web-build-deploy/SKILL.mdsource content
Web Build & Deploy (React)
Vercel Deployment
Quick Deploy
# Install Vercel CLI npm i -g vercel # Deploy vercel # Deploy to production vercel --prod
Configuration (vercel.json)
{ "buildCommand": "npm run build", "outputDirectory": "dist", "framework": "vite", "rewrites": [ { "source": "/(.*)", "destination": "/" } ], "headers": [ { "source": "/(.*)", "headers": [ { "key": "X-Content-Type-Options", "value": "nosniff" }, { "key": "X-Frame-Options", "value": "DENY" } ] } ] }
Environment Variables
# Add via CLI vercel env add NEXT_PUBLIC_API_URL # Or in Vercel dashboard: Settings > Environment Variables # Access in code const apiUrl = process.env.NEXT_PUBLIC_API_URL;
Preview Deployments
Every push to a branch creates a preview URL automatically:
https://project-git-branch-username.vercel.app
Netlify Deployment
Quick Deploy
# Install Netlify CLI npm i -g netlify-cli # Login netlify login # Deploy preview netlify deploy # Deploy to production netlify deploy --prod
Configuration (netlify.toml)
[build] command = "npm run build" publish = "dist" [[redirects]] from = "/*" to = "/index.html" status = 200 [[headers]] for = "/*" [headers.values] X-Frame-Options = "DENY" X-Content-Type-Options = "nosniff" [build.environment] NODE_VERSION = "18"
Environment Variables
# Add via CLI netlify env:set API_URL https://api.example.com # Or in Netlify dashboard: Site settings > Environment variables
Docker Deployment
Dockerfile (Multi-stage build)
# Build stage FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Production stage FROM nginx:alpine # Copy built files COPY --from=builder /app/dist /usr/share/nginx/html # Copy nginx config for SPA routing COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
nginx.conf (SPA routing)
server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; # Gzip compression gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml; # Cache static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; } # SPA fallback location / { try_files $uri $uri/ /index.html; } # Security headers add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; }
Docker Compose
# docker-compose.yml version: '3.8' services: web: build: . ports: - "80:80" environment: - NODE_ENV=production restart: unless-stopped # With backend api: build: ./backend ports: - "8000:8000" environment: - DATABASE_URL=postgresql://user:pass@db:5432/app depends_on: - db db: image: postgres:15-alpine volumes: - postgres_data:/var/lib/postgresql/data environment: - POSTGRES_USER=user - POSTGRES_PASSWORD=pass - POSTGRES_DB=app volumes: postgres_data:
Build & Run
# Build image docker build -t myapp:latest . # Run container docker run -p 80:80 myapp:latest # With docker-compose docker-compose up -d docker-compose down
GitHub Actions CI/CD
Basic Workflow
# .github/workflows/deploy.yml name: Deploy on: push: branches: [main] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v4 with: node-version: '18' cache: 'npm' - name: Install dependencies run: npm ci - name: Run tests run: npm test - name: Build run: npm run build env: VITE_API_URL: ${{ secrets.API_URL }} - name: Deploy to Vercel if: github.ref == 'refs/heads/main' uses: amondnet/vercel-action@v25 with: vercel-token: ${{ secrets.VERCEL_TOKEN }} vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} vercel-args: '--prod'
With Preview Deployments
name: Preview on: [pull_request] jobs: preview: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Deploy Preview uses: amondnet/vercel-action@v25 id: deploy with: vercel-token: ${{ secrets.VERCEL_TOKEN }} vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} - name: Comment PR uses: actions/github-script@v6 with: script: | github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: '🚀 Preview deployed to: ${{ steps.deploy.outputs.preview-url }}' })
Environment Configuration
Vite
// vite.config.ts import { defineConfig, loadEnv } from 'vite'; export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), ''); return { define: { 'process.env.API_URL': JSON.stringify(env.VITE_API_URL), }, }; });
# .env.development VITE_API_URL=http://localhost:8000 # .env.production VITE_API_URL=https://api.example.com
Next.js
# .env.local (not committed) DATABASE_URL=postgresql://localhost:5432/dev # .env.production NEXT_PUBLIC_API_URL=https://api.example.com
// Access in code // Client-side (must start with NEXT_PUBLIC_) const apiUrl = process.env.NEXT_PUBLIC_API_URL; // Server-side only const dbUrl = process.env.DATABASE_URL;
Build Optimization
Vite Build Analysis
# Install analyzer npm i -D rollup-plugin-visualizer # Add to vite.config.ts import { visualizer } from 'rollup-plugin-visualizer'; export default defineConfig({ plugins: [ visualizer({ filename: 'stats.html', open: true, }), ], }); # Build and analyze npm run build
Code Splitting
// Route-based splitting const Dashboard = lazy(() => import('./pages/Dashboard')); const Settings = lazy(() => import('./pages/Settings')); // Component splitting const HeavyChart = lazy(() => import('./components/HeavyChart'));
Caching Strategy
// vite.config.ts export default defineConfig({ build: { rollupOptions: { output: { manualChunks: { vendor: ['react', 'react-dom'], router: ['react-router-dom'], ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'], }, }, }, }, });
Health Checks & Monitoring
Health Check Endpoint
// For Docker/Kubernetes // api/health.ts (Next.js) export async function GET() { return Response.json({ status: 'healthy', timestamp: Date.now() }); }
Error Tracking (Sentry)
// sentry.client.config.ts import * as Sentry from '@sentry/react'; Sentry.init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, environment: process.env.NODE_ENV, tracesSampleRate: 0.1, }); // Wrap app const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
Common Issues
| Issue | Solution |
|---|---|
| 404 on refresh (SPA) | Configure server for SPA fallback |
| Environment vars undefined | Check prefix (VITE_, NEXT_PUBLIC_) |
| Build fails on CI | Check Node version, clear cache |
| Docker image too large | Use multi-stage build, alpine base |
| Slow builds | Enable caching, parallelize |
Deployment Checklist
Before deploying to production:
- Environment variables set
- Build succeeds locally
- Tests pass
- Security headers configured
- Error tracking enabled
- Performance optimized (bundle size, code splitting)
- SEO meta tags (if applicable)
- SSL/HTTPS enabled
- Custom domain configured
- Health check endpoint working