Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cubby.pro/llms.txt

Use this file to discover all available pages before exploring further.

PWA Support

Every app deployed to Cubby is automatically installable as a Progressive Web App (PWA). Users can add your app to their home screen on iOS, Android, and desktop — no App Store required.

What Cubby Does Automatically

When you deploy an app, Cubby auto-generates a web app manifest at /api/manifest/<appId>. This manifest tells the browser how to install your app as a PWA. The auto-generated manifest includes:
  • App name (from your cubby.yaml)
  • Display mode: standalone (runs without browser UI)
  • Theme color: Deterministic color based on your app name
  • Icons: Multiple sizes (72x72 through 512x512) for all devices
  • Orientation: Portrait-primary
During the deploy verification pipeline, Cubby also:
  • Generates icons if your project is missing the required 192x192 and 512x512 PNG icons
  • Generates manifest.json if none exists in your project
  • Warns you if your layout file is missing a <link rel="manifest"> tag
These auto-fixes appear in your deploy report as icons-generated, manifest-generated, and manifest-link-injected.

Installing Your App

Once deployed, users can install your app from the browser:

iOS (Safari)

  1. Open your app URL in Safari
  2. Tap the Share button (square with arrow)
  3. Scroll down and tap Add to Home Screen
  4. Tap Add
The app appears as an icon on the home screen and opens in standalone mode (no Safari UI).

Android (Chrome)

  1. Open your app URL in Chrome
  2. Chrome may show an install banner automatically
  3. Or tap the three-dot menu and select Add to Home Screen
  4. Tap Install

Desktop (Chrome)

  1. Open your app URL in Chrome
  2. Click the install icon in the address bar (monitor with down arrow)
  3. Click Install
PWA install requires HTTPS, which Cubby provides automatically for all deployed apps.

Adding Offline Support

Offline support is not automatic. By default, your app requires an internet connection. To enable offline support, you need to add a service worker. Here’s a basic service worker with a cache-first strategy:
// public/sw.js
const CACHE_VERSION = 'v1.0.0' // Change on each deploy!
const CACHE_NAME = `my-app-cache-${CACHE_VERSION}`

const PRECACHE_URLS = [
  '/',
  '/offline.html',
]

// Install: cache essential assets
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE_URLS))
  )
  self.skipWaiting()
})

// Activate: clean up old caches
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
    )
  )
  self.clients.claim()
})

// Fetch: cache-first for precached assets, network-first for everything else
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((cached) => cached || fetch(event.request))
  )
})
Register it in your app:
// app/layout.tsx or a client component
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
}
For Next.js apps, consider using next-pwa which handles service worker generation and caching strategies automatically.

Service Worker Caching Best Practices

If your service worker uses a static cache name, users may see stale content after you redeploy. This is especially common on iOS Safari PWAs.

The Problem

When your service worker opens a cache with a hardcoded name like this:
// BAD: Static cache name
caches.open('my-cache')
The browser serves old cached assets even after you deploy new code. iOS Safari is particularly affected because it does not check for service worker updates as aggressively as Chrome. Users running your app as a home screen PWA on iOS may see the old version indefinitely.

The Solution

Version your cache names using a build hash or version number:
// GOOD: Versioned cache name
const BUILD_HASH = '20260402a' // Update on each deploy
caches.open(`my-cache-${BUILD_HASH}`)

How It Works

  1. You deploy a new version with a new BUILD_HASH
  2. The browser detects the service worker file has changed
  3. The new SW installs and opens a cache with the new name (e.g., my-cache-20260402a)
  4. The activate event deletes old caches (e.g., my-cache-20260401a)
  5. Users get fresh assets on next load

Example: Properly Versioned Service Worker

// public/sw.js
const BUILD_HASH = process.env.NEXT_PUBLIC_BUILD_ID || Date.now().toString()
const CACHE_NAME = `app-cache-${BUILD_HASH}`

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) =>
      cache.addAll(['/', '/offline.html'])
    )
  )
  self.skipWaiting()
})

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(
        keys
          .filter((key) => key.startsWith('app-cache-') && key !== CACHE_NAME)
          .map((key) => caches.delete(key))
      )
    )
  )
  self.clients.claim()
})
Cubby’s deploy verification pipeline warns you if it detects a service worker with a static cache name. Look for the sw-cache-versioning check in your deploy report.

Customizing Your PWA

To override the auto-generated manifest, include your own manifest.json in your project’s public/ directory. Cubby respects user-provided manifests and will not overwrite them. Your custom manifest should include at minimum:
{
  "name": "My App",
  "short_name": "My App",
  "start_url": "/",
  "display": "standalone",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}
Link it in your layout:
<link rel="manifest" href="/manifest.json" />
If your manifest exists but is missing required fields or icon sizes, the verification pipeline will auto-fix them during deploy.