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.

Deployment Checklist
- Ensure
packages/contractsandpackages/sharedare built (the client depends on both) - Set the correct
VITE_CHAIN_IDfor the target environment in the root.env - Verify all required environment variables are configured (see Build Environments)
- Build the client:
VITE_CHAIN_ID=42161 bun run build:client - Deploy static output from
packages/client/dist/to your CDN or static hosting provider - Configure cache-busting headers for
index.htmland long-lived caching for hashed static assets - Verify the service worker registers under
/home, preserves the Workbox precache, and serves the app shell for/homeroutes. - 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:
packages/contracts-- ABI JSON files and deployment artifactspackages/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:
| Variable | Purpose |
|---|---|
VITE_CHAIN_ID | Target chain (e.g., 11155111 for Sepolia, 42161 for Arbitrum) |
VITE_WALLETCONNECT_PROJECT_ID | WalletConnect authentication |
VITE_PIMLICO_API_KEY | Pimlico bundler/paymaster for account abstraction |
VITE_ENVIO_INDEXER_URL | Envio GraphQL endpoint |
VITE_API_BASE_URL | Agent API origin for public API calls and signed upload URLs |
VITE_POSTHOG_KEY | Client/PostHog browser analytics token |
VITE_ALCHEMY_API_KEY | Optional RPC provider key for higher-capacity reads |
VITE_GOOGLE_APPOINTMENT_URL | Optional 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 --
/homenavigations 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
useServiceWorkerUpdatehook 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:
- Detect waiting service worker
- Show update prompt to user
- On accept, send
SKIP_WAITINGmessage - 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:
| Variable | Purpose |
|---|---|
POSTHOG_CLIENT_ENV_ID | PostHog environment ID for the client app |
POSTHOG_CLI_TOKEN | PostHog 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