vite-plugin-pwa-manifest
v0.3.0
Published
Vite plugin to generate a PWA web app manifest with env-aware metadata and a dev no-store preview route.
Maintainers
Readme
vite-plugin-pwa-manifest
Generate a PWA web app manifest for any Vite app — env-aware build metadata, a fully-typed config, and a no-store dev preview that honors your Vite
base. Zero dependencies, ESM + CJS.
Features
- 🧾 Generates a valid
manifest.jsonat build time (emitted to your buildoutDir). - 💾 Optional on-disk mirror (
outputDir) for static hosting / CDNs — written only when changed. - 🧪 Dev preview with
no-store+ weakETag/304, served at the correct route under a custombase. - 🏷️ Build metadata out of the box:
version(git tag/commit),pkgVersion,buildTime,mode. - 🎛️ Full
transform(json, { mode })hook for complete control. - 🖼️ Icon normalization + de-duplication, with a one-time warning when no maskable icon is present.
- 🟢 Zero runtime dependencies.
Installation
npm i -D vite-plugin-pwa-manifest
# or: pnpm add -D vite-plugin-pwa-manifest
# or: yarn add -D vite-plugin-pwa-manifestRequires Node ≥ 18 and Vite ≥ 4.
Usage
// vite.config.ts
import { defineConfig } from 'vite';
import generateManifest from 'vite-plugin-pwa-manifest';
export default defineConfig({
plugins: [
generateManifest({
name: 'My App',
short_name: 'App',
description: 'Example PWA with Vite',
theme_color: '#0a131b',
icons: [
{ src: '/icons/icon-192x192.png', sizes: '192x192', type: 'image/png', purpose: 'any maskable' },
{ src: '/icons/icon-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'any maskable' },
],
outputDir: 'public', // optional: also write public/manifest.json
transform(json, { mode }) {
if (mode === 'development') json.name = 'My App (dev)';
return json;
},
}),
],
});Reference it from your HTML:
<link rel="manifest" href="/manifest.json" />| Phase | Behavior |
|-------|----------|
| dev (vite) | Served at the manifest.json route under your Vite base with Cache-Control: no-store + weak ETag (304 on If-None-Match). |
| build (vite build) | Emitted as a build asset to the root of your build outDir. |
| outputDir | Also written to disk (dev & build), skipped when unchanged. |
Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| filename | string | "manifest.json" | Output filename. |
| outputDir | string \| false | false | Mirror the manifest to this directory on disk. |
| name / short_name | string | "My App" / "App" | Display names. |
| description | string | — | App description. |
| lang / dir | string / "ltr" \| "rtl" | "en" / "ltr" | Language & direction. |
| start_url / scope / id | string | "/" | Each is normalized to a leading slash. |
| display | "standalone" \| "fullscreen" \| "minimal-ui" \| "browser" | "standalone" | Display mode. |
| orientation | "any" \| "portrait" \| "landscape" | — | Orientation. |
| background_color | string | falls back to theme_color | Splash background. |
| theme_color | string | "#0a131b" | Theme color. |
| categories | string[] | — | App store categories. |
| prefer_related_applications / related_applications | — | — | Native app hints. |
| protocol_handlers | Array<{ protocol; url }> | — | Protocol handlers. |
| icons | ManifestIcon[] | 192/512 maskable | Icons (normalized + de-duped). |
| screenshots | Screenshot[] | — | Screenshots. |
| extra | Record<string, unknown> | — | Extra fields merged into the manifest. |
| includeBuildMeta | boolean | true | Add pkgVersion, version, buildTime, mode. |
| transform | (json, { mode }) => json | — | Final transform over the assembled manifest. |
Note:
start_url,scope, andidare always normalized to begin with/. Fields leftundefinedare omitted from the output.
Types
type ManifestIcon = {
src: string;
sizes: string;
type?: string;
purpose?: 'any' | 'maskable' | 'monochrome' | 'any maskable';
};
type Screenshot = {
src: string;
sizes?: string;
type?: string;
label?: string;
form_factor?: 'wide' | 'narrow';
};Build metadata
With includeBuildMeta (default true) the plugin appends:
| Field | Source |
|-------|--------|
| version | nearest git tag, else git describe, else short commit, else pkgVersion. |
| pkgVersion | version from your package.json. |
| buildTime | ISO timestamp of the build. |
| mode | "development" in dev, "production" in build. |
Git is detected via a statically-imported
execSync(not a dynamicrequire), so detection works correctly in both the ESM and CJS builds. Outside a git work tree these fields simply fall back topkgVersion.
Example output
{
"lang": "en",
"dir": "ltr",
"name": "My App",
"short_name": "App",
"description": "Example PWA with Vite",
"start_url": "/",
"scope": "/",
"id": "/",
"display": "standalone",
"background_color": "#0a131b",
"theme_color": "#0a131b",
"icons": [
{ "src": "/icons/icon-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" },
{ "src": "/icons/icon-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
],
"pkgVersion": "1.4.0",
"version": "v1.4.0",
"buildTime": "2025-09-09T13:45:00.000Z",
"mode": "production"
}License
MIT © dev.zarghami
