I am not building an iOS app. Not out of principle, out of math. An app means Xcode, App Store review, a $99/year membership, and a release train that moves in days. This site deploys in about a minute, several times a day, every time I push. The moment I wrap it in an app, the app is the stale copy of a thing that updates itself.
But I still want it on the home screen, full-screen, with its own icon, opening like an app instead of a browser tab. That exact thing exists, it has since forever, and it takes three small files: a web app manifest, an Apple touch icon, and two lines of iOS metadata. Here is the whole build, copied from this site’s actual source.
~1
minute to ship
every push, straight to prod
0
review queues
no store between us
$0
per year
the membership stays unpaid
2
meta lines for iOS
the part Apple reads
What you get, what you give up
Installed this way, the site gets its own icon on the springboard, its own card in the app switcher, and a standalone window with zero Safari chrome: no URL bar, no tabs, just the page. Updates ship by deploying the site; nobody taps “update”, nobody reviews anything. Since iOS 16.4 an installed web app can even do push notifications if you ever wire them up.
What you give up: App Store discovery (irrelevant, my distribution is TikTok and X, not search-in-a-store) and the deeper native APIs I was never going to use. For a content-and-commerce site, the trade is not close.
push to prod · the phone keeps up
01
manifest.ts
name, icons, display mode
02
Icon at build
one generated gradient mark
03
Two iOS meta lines
the part Apple actually reads
04
Add to Home Screen
no store, no review, no $99
Step 1: the manifest
The manifest is what makes a browser treat your site as installable. In Next.js it’s one typed file at app/manifest.ts: the framework serves it and links it from every page. display: 'standalone' is the line that removes the browser chrome; black background and theme colours keep the launch moment seamless on a black site.
// app/manifest.ts
import type { MetadataRoute } from 'next'
export default function manifest(): MetadataRoute.Manifest {
return {
name: 'Alex Saint',
short_name: 'Alex Saint',
description: 'I make viral AI video, and I sell the prompts behind it.',
start_url: '/',
display: 'standalone',
background_color: '#000000',
theme_color: '#000000',
icons: [
{ src: '/icon.svg', sizes: 'any', type: 'image/svg+xml' },
{ src: '/apple-icon', sizes: '180x180', type: 'image/png' },
],
}
}Step 2: the home-screen icon
iOS ignores SVG touch icons, so the home-screen icon has to be a PNG. Instead of exporting one from a design tool and letting it rot, Next renders it at build time from JSX: app/apple-icon.tsx returns an ImageResponse, and the //A mark is drawn from the same coordinates as the favicon. One source of truth, two icons.
// app/apple-icon.tsx
import { ImageResponse } from 'next/og'
export const size = { width: 180, height: 180 }
export const contentType = 'image/png'
export default function AppleIcon() {
return new ImageResponse(
(
<div style={{ width: '100%', height: '100%', display: 'flex',
alignItems: 'center', justifyContent: 'center',
background: '#000000' }}>
<svg width="180" height="180" viewBox="0 0 100 100">
{/* the //A mark, pink → gold */}
</svg>
</div>
),
size
)
}One trap: don’t pre-round the corners. iOS applies its own superellipse mask to every home-screen icon, so a pre-rounded icon shows dark notches where the two masks disagree. Ship it full-bleed square and let the OS do the rounding.
Step 3: tell iOS it’s an app
Two additions to the root layout’s metadata finish the job. appleWebApp opts the site into standalone mode under its own name, and the viewport’s themeColor keeps the status bar black so the app edge-to-edge matches the page.
// app/layout.tsx
export const metadata: Metadata = {
// ...
appleWebApp: {
capable: true,
title: 'Alex Saint',
statusBarStyle: 'black-translucent',
},
}
export const viewport: Viewport = {
themeColor: '#000000',
}black-translucent means the page flows under the status bar, which looks incredible on a black site and terrible on a white one. If your background isn’t dark, use 'default'.
Installing it
Chrome’s own walkthrough of what installation does on each platform is the clearest one I have found, and it is worth two minutes before you ship a manifest of your own.
- 01Open alexsssaint.com in Safari on your iPhone.
- 02Tap the Share button, the square with the arrow.
- 03Tap "Add to Home Screen". The //A icon and the name come from the files above.
- 04Tap Add. Launch it from the home screen: full-screen, black status bar, no browser.
The one thing to design for
Standalone mode has no back button and no refresh: your site’s own navigation is all the navigation there is. That is why every page here carries the fixed avatar chip in the top corner: in the installed app it’s not decoration, it’s the way home. If your site leans on the browser’s back button, fix that before you ship a manifest.
That’s the entire build. No App Store, no review queue, no second codebase. The site is the app, and every git push is a release.
