PWA-first: How we made every Hyperdrift app installable
A short engineering note on why we did this and exactly how.
Every app we build should feel like a real product — not a tab you have to remember to open.
Progressive Web Apps let users pin any web application to their home screen and launch it in standalone mode, without browser chrome. It is one of the cheapest upgrades you can make to perceived product quality, and we had not done it anywhere. That changed this week.
What we added to each app
Three files, per app:
src/app/manifest.ts — the web manifest, handled natively by Next.js App Router. Drop this file, next.js auto-generates /manifest.webmanifest and injects <link rel="manifest"> into every page's <head>. No layout edits needed.
src/app/icon.tsx — a dynamic icon using Next.js's built-in ImageResponse API. This generates a 512×512 PNG at the /icon route — no binary files to commit, no external tooling. The manifest references this route directly.
That is the entire implementation. No service worker, no offline cache, no install prompt engineering. The browser handles install eligibility once the manifest and icon are in place.
The pattern
// src/app/manifest.ts
export default function manifest() {
return {
name: 'Capital Engine',
short_name: 'Capital Engine',
description: 'DeFi yield intelligence. 8,000+ pools ranked by capital efficiency.',
start_url: '/',
display: 'standalone',
background_color: '#070605',
theme_color: '#39d48f',
icons: [
{
src: '/icon',
sizes: '512x512',
type: 'image/png',
purpose: 'any',
},
],
}
}
// src/app/icon.tsx
import { ImageResponse } from 'next/og'
export const size = { width: 512, height: 512 }
export const contentType = 'image/png'
export default function Icon() {
return new ImageResponse(
(
<div style={{ width: '100%', height: '100%', display: 'flex',
alignItems: 'center', justifyContent: 'center', background: '#070605' }}>
<div style={{ width: 400, height: 400, borderRadius: 80,
background: '#0e1f18', border: '6px solid #39d48f',
display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<span style={{ color: '#39d48f', fontSize: 160, fontWeight: 800,
fontFamily: 'sans-serif', letterSpacing: '-6px' }}>
CE
</span>
</div>
</div>
),
{ ...size }
)
}
Same pattern across all five apps. Only the colors and label change.
Apps covered
| App | Theme colour | Icon label | URL |
|---|---|---|---|
| Capital Engine | #39d48f (mint) | CE | web3.hyperdrift.io |
| HyperCV | #d4930c (amber) | CV | cv.hyperdrift.io |
| Intel | #8438FF (violet) | I | intel.hyperdrift.io |
| Revela | #FFC15E (warm amber) | R | revela.club |
| HyperDrift | #F5C842 (gold) | HD | hyperdrift.io |
What this unlocks
- Install prompt on Android Chrome — after the user interacts with the site, the browser shows an "Add to Home Screen" banner automatically.
- Standalone launch mode — opens without browser chrome, full-screen feel.
- Correct icon in app switcher — no more blank favicon squares.
- iOS Add to Home Screen — works via Safari's share sheet. The manifest drives the name and background colour; apple-touch-icon falls back to the icon route.
What we did not add (and why)
Service worker / offline support — not worth the complexity for data-heavy apps. Capital Engine needs live yield data. Intel needs today's posts. Caching stale data would actively mislead users.
Install prompt UI — browsers manage this natively once eligibility criteria are met. We do not want to interrupt the flow with a custom install button.
Separate 192×192 and 512×512 PNG files — the ImageResponse approach generates a single high-resolution icon that scales cleanly. Static PNG files are only needed if you need pixel-perfect assets at specific sizes or maskable icons for Android.
Also shipped: web3-capital production fix
A BigInt literal (0n) in TokenBalances.tsx was causing the TypeScript compiler to fail on the production build — the tsconfig.json had no target, which defaults to ES5 where BigInt literals are not available.
Fix: added "target": "ES2020" to the web3-capital tsconfig.json. The app was building locally (SWC ignores this) but failing in production (where tsc runs for type checking). One line, full build restored.
Next: proper maskable PNG icons to support Android's adaptive icon system. These require a safe-zone design (content in the inner 80% of the canvas) and should be committed as static assets. For now, the dynamic icon route is sufficient for basic PWA install across all platforms.
Get weekly intel — courtesy of intel.hyperdrift.io