Skip to main content

Client PWA Deployment

The client is an offline-first Progressive Web App built with Vite and React. Deployment produces a static build with service worker support for offline functionality.

Client PWA — passkey login with biometric authentication

Deployment Checklist

  1. Ensure packages/contracts and packages/shared are built (the client depends on both)
  2. Set the correct VITE_CHAIN_ID for the target environment in the root .env
  3. Verify all required environment variables are configured (see Build Environments)
  4. Build the client: VITE_CHAIN_ID=42161 bun run build:client
  5. Deploy static output from packages/client/dist/ to your CDN or static hosting provider
  6. Configure cache-busting headers for index.html and long-lived caching for hashed static assets
  7. Verify the service worker registers under /home, preserves the Workbox precache, and serves the app shell for /home routes.
  8. Run Lighthouse audit to check Performance, Accessibility, PWA readiness, and SEO

Build Environments

Build Dependencies

The client depends on packages built earlier in the dependency chain:

  1. packages/contracts -- ABI JSON files and deployment artifacts
  2. packages/shared -- React hooks, modules, types, and components

Both must be built before the client. The monorepo's bun build command handles this order automatically.

Environment Variables

All environment variables come from the root .env file (never package-specific). Key variables for the client build:

VariablePurpose
VITE_CHAIN_IDTarget chain (e.g., 11155111 for Sepolia, 42161 for Arbitrum)
VITE_WALLETCONNECT_PROJECT_IDWalletConnect authentication
VITE_PIMLICO_API_KEYPimlico bundler/paymaster for account abstraction
VITE_ENVIO_INDEXER_URLEnvio GraphQL endpoint
VITE_API_BASE_URLAgent API origin for public API calls and signed upload URLs
VITE_POSTHOG_KEYClient/PostHog browser analytics token
VITE_ALCHEMY_API_KEYOptional RPC provider key for higher-capacity reads
VITE_GOOGLE_APPOINTMENT_URLOptional public appointment booking URL

The client Vercel project is a static Vite app. Do not provision database variables such as DATABASE_URL, POSTGRES_*, PG*, or NEON_PROJECT_ID there; browser and public API flows go through VITE_API_BASE_URL and the deployed agent. The PostHog host is fixed in shared runtime config, so VITE_POSTHOG_HOST is not read by the app. The client build also does not read VITE_THIRDWEB_CLIENT_ID; thirdweb checkout configuration belongs to the agent runtime.

Single-Chain Build

The build is single-chain: VITE_CHAIN_ID determines which deployment artifact and chain configuration is baked in at build time.

Making A Deployment

Build Process

# Build for production
VITE_CHAIN_ID=42161 bun run build:client

Static Hosting

The client build outputs to packages/client/dist/. This is a fully static site deployable to any CDN or static hosting provider.

For browser-origin deploys such as www.greengoods.app, the static asset base remains /, while the PWA manifest scope and service worker scope are /home. Public/editorial routes remain browser-owned at /, /gardens, /fund, /impact, and /actions.

CI Build

The client.yml lane builds the client with test configuration:

- name: Build client for E2E
run: bun run build:client
env:
VITE_USE_HASH_ROUTER: "true"
VITE_CHAIN_ID: "11155111"

VITE_USE_HASH_ROUTER enables hash-based routing for environments where server-side routing is not available (e.g., IPFS hosting). In that build, app routes are represented as ./#/home, ./#/home/garden, and ./#/home/profile while assets stay relative.

Service Worker

The PWA service worker enables offline functionality -- the core differentiator of the client app. Key behaviors:

  • Precaching -- Static assets are cached at install time
  • App shell fallback -- /home navigations fall back to the precached app shell when offline
  • Runtime caching -- API responses are cached with stale-while-revalidate
  • Background sync -- Work submissions queue when offline and sync when connectivity returns
  • Update detection -- The useServiceWorkerUpdate hook detects new versions and prompts users

Production browser-origin builds register the service worker with scope /home and run one-time cleanup for older root-scoped Green Goods registrations. The custom worker preserves Workbox precaches, clears stale runtime caches, and forces public/editorial navigations to reload from the network so old app-shell caches do not capture browser pages.

Service Worker Update Flow

The shared hook useServiceWorkerUpdate handles the update lifecycle:

  1. Detect waiting service worker
  2. Show update prompt to user
  3. On accept, send SKIP_WAITING message
  4. Reload the page with the new version

Offline-First Architecture

The client is designed to work without internet connectivity:

  • IndexedDB stores drafts, work submissions, and cached garden data via the job queue system
  • Draft auto-save persists form state every few seconds
  • Background sync processes queued mutations when connectivity returns
  • Optimistic updates show pending changes immediately in the UI

This architecture means the deployment must ensure the service worker and precached assets are delivered correctly. Cache-busting headers should be set for index.html but long-lived caching for hashed static assets.

Performance Monitoring

The client.yml lane has a manual Lighthouse advisory job checking:

  • Performance score
  • Accessibility compliance
  • PWA readiness
  • SEO basics

Source Maps

The client Vercel project runs only bun run build. The trusted client.yml workflow owns PostHog source-map upload on main; its upload credentials are GitHub repository secrets, not Vercel project variables.

Set these variables as GitHub repository secrets:

VariablePurpose
POSTHOG_CLIENT_ENV_IDPostHog environment ID for the client app
POSTHOG_CLI_TOKENPostHog token with source-map upload permissions

Production Vercel deploys do not require these PostHog variables. The GitHub Actions source-map upload job fails closed if the PostHog source-map variables are missing. GG_ENABLE_SOURCEMAPS is an upload-lane flag, not a durable frontend Vercel setting; current Vite source-map emission is still coupled to SENTRY_AUTH_TOKEN, so keep client Sentry integration variables only where Sentry upload or log-drain integration is intentionally enabled.

Resources

Next page

Next: Deploy the Admin Dashboard

The admin dashboard shares the same build toolchain as the client but targets garden operators with role-based access.

Admin Dashboard Deployment