Zero-config PWA plugin for Vite that enables offline functionality, service workers, and web app manifest generation.
—
Virtual modules providing access to PWA information, generated assets, and metadata for integrations and custom implementations.
Access to PWA configuration and runtime information.
// Virtual module: virtual:pwa-info
/**
* PWA information object containing web manifest and service worker data
* Available only when PWA is enabled and not in SSR builds
*/
const pwaInfo: PwaInfo | undefined;
interface PwaInfo {
/** Whether PWA is running in development environment */
pwaInDevEnvironment: boolean;
/** Web manifest information - always present when pwaInfo is available */
webManifest: {
/** URL to the web manifest file */
href: string;
/** Whether to use credentials for manifest requests */
useCredentials: boolean;
/** Complete link tag for the web manifest */
linkTag: string;
};
/** Service worker registration information - conditionally present */
registerSW?: {
/** @deprecated Use mode instead */
inline: boolean;
/** Registration mode - inline script or external script */
mode: 'inline' | 'script' | 'script-defer';
/** Path for inline service worker registration script */
inlinePath: string;
/** Path for external service worker registration script */
registerPath: string;
/** Service worker scope */
scope: string;
/** Service worker type */
type: 'classic' | 'module';
/** Complete script tag for service worker registration */
scriptTag?: string;
};
}Usage Example:
import { pwaInfo } from "virtual:pwa-info";
// Use in framework integrations or custom implementations
if (pwaInfo) {
console.log("PWA is enabled");
console.log("Manifest URL:", pwaInfo.webManifest.href);
// Add manifest link to head
document.head.insertAdjacentHTML("beforeend", pwaInfo.webManifest.linkTag);
// Handle service worker registration
if (pwaInfo.registerSW && pwaInfo.registerSW.scriptTag) {
document.head.insertAdjacentHTML("beforeend", pwaInfo.registerSW.scriptTag);
}
} else {
console.log("PWA is disabled or in SSR mode");
}
// Framework integration example
function usePWAInfo() {
return {
isPWAEnabled: !!pwaInfo,
isDevEnvironment: pwaInfo?.pwaInDevEnvironment ?? false,
manifestUrl: pwaInfo?.webManifest.href,
hasServiceWorker: !!pwaInfo?.registerSW,
};
}Access to generated PWA assets for HTML head injection.
// Virtual module: virtual:pwa-assets/head
/**
* PWA assets head configuration with links and theme color
* Available when PWA assets generation is enabled
*/
const pwaAssetsHead: PWAAssetsHead;
interface PWAAssetsHead {
/** Array of head link elements for PWA assets */
links: PWAAssetHeadLink[];
/** Theme color meta tag configuration */
themeColor?: ColorSchemeMeta;
}
interface PWAAssetHeadLink {
/** Optional link ID for targeting */
id?: string;
/** Link relationship type */
rel: 'apple-touch-startup-image' | 'apple-touch-icon' | 'icon';
/** Asset URL */
href: string;
/** Media query for responsive assets */
media?: string;
/** Icon sizes specification */
sizes?: string;
/** MIME type of the asset */
type?: string;
}
interface ColorSchemeMeta {
/** Meta tag name attribute */
name: string;
/** Meta tag content attribute */
content: string;
}Usage Example:
import { pwaAssetsHead } from "virtual:pwa-assets/head";
// Inject PWA assets into HTML head
function injectPWAAssets() {
const head = document.head;
// Inject icon and splash screen links
pwaAssetsHead.links.forEach((link) => {
const linkElement = document.createElement("link");
linkElement.rel = link.rel;
linkElement.href = link.href;
if (link.id) linkElement.id = link.id;
if (link.sizes) linkElement.setAttribute("sizes", link.sizes);
if (link.type) linkElement.type = link.type;
if (link.media) linkElement.media = link.media;
head.appendChild(linkElement);
});
// Inject theme color meta tag
if (pwaAssetsHead.themeColor) {
const metaElement = document.createElement("meta");
metaElement.name = pwaAssetsHead.themeColor.name;
metaElement.content = pwaAssetsHead.themeColor.content;
head.appendChild(metaElement);
}
}
// Framework integration example (React)
function PWAHeadAssets() {
return (
<>
{pwaAssetsHead.links.map((link, index) => (
<link
key={link.id || index}
rel={link.rel}
href={link.href}
{...(link.sizes && { sizes: link.sizes })}
{...(link.type && { type: link.type })}
{...(link.media && { media: link.media })}
/>
))}
{pwaAssetsHead.themeColor && (
<meta
name={pwaAssetsHead.themeColor.name}
content={pwaAssetsHead.themeColor.content}
/>
)}
</>
);
}Access to generated PWA icon assets organized by type and purpose.
// Virtual module: virtual:pwa-assets/icons
/**
* PWA assets icons collection organized by type
* Available when PWA assets generation is enabled
*/
const pwaAssetsIcons: PWAAssetsIcons;
interface PWAAssetsIcons {
/** Favicon icons for browser tabs */
favicon: Record<string, PWAAssetIcon<FaviconLink>>;
/** Transparent background icons */
transparent: Record<string, PWAAssetIcon<HtmlLink>>;
/** Maskable icons for adaptive icon support */
maskable: Record<string, PWAAssetIcon<HtmlLink>>;
/** Apple-specific touch icons */
apple: Record<string, PWAAssetIcon<HtmlLink>>;
/** Apple splash screen images */
appleSplashScreen: Record<string, PWAAssetIcon<AppleSplashScreenLink>>;
}
type PWAAssetIcon<T extends HtmlLink> = Omit<IconAsset<T>, 'buffer'>;
interface IconAsset<T extends HtmlLink> {
/** Asset name/identifier */
name: string;
/** Asset file path */
src: string;
/** Icon dimensions */
size: { width: number; height: number };
/** HTML link configuration */
htmlLink: T;
}
interface HtmlLink {
rel: string;
href: string;
sizes?: string;
type?: string;
}
interface FaviconLink extends HtmlLink {
rel: 'icon';
}
interface AppleSplashScreenLink extends HtmlLink {
rel: 'apple-touch-startup-image';
media: string;
}Usage Example:
import { pwaAssetsIcons } from "virtual:pwa-assets/icons";
// Access different icon types
function logIconAssets() {
console.log("Favicon icons:", Object.keys(pwaAssetsIcons.favicon));
console.log("Transparent icons:", Object.keys(pwaAssetsIcons.transparent));
console.log("Maskable icons:", Object.keys(pwaAssetsIcons.maskable));
console.log("Apple icons:", Object.keys(pwaAssetsIcons.apple));
console.log("Apple splash screens:", Object.keys(pwaAssetsIcons.appleSplashScreen));
}
// Create manifest icons from generated assets
function createManifestIcons() {
const icons = [];
// Add transparent icons to manifest
Object.values(pwaAssetsIcons.transparent).forEach((icon) => {
icons.push({
src: icon.src,
sizes: `${icon.size.width}x${icon.size.height}`,
type: icon.htmlLink.type || "image/png",
purpose: "any",
});
});
// Add maskable icons to manifest
Object.values(pwaAssetsIcons.maskable).forEach((icon) => {
icons.push({
src: icon.src,
sizes: `${icon.size.width}x${icon.size.height}`,
type: icon.htmlLink.type || "image/png",
purpose: "maskable",
});
});
return icons;
}
// Framework integration example (Vue)
function usePWAIcons() {
return {
favicon: computed(() => pwaAssetsIcons.favicon),
transparent: computed(() => pwaAssetsIcons.transparent),
maskable: computed(() => pwaAssetsIcons.maskable),
apple: computed(() => pwaAssetsIcons.apple),
appleSplashScreen: computed(() => pwaAssetsIcons.appleSplashScreen),
// Helper to get icon by size
getIconBySize: (type: keyof PWAAssetsIcons, size: string) => {
const icons = pwaAssetsIcons[type];
return Object.values(icons).find(
(icon) => `${icon.size.width}x${icon.size.height}` === size
);
},
// Helper to create HTML links
createHtmlLinks: (type: keyof PWAAssetsIcons) => {
return Object.values(pwaAssetsIcons[type]).map((icon) => icon.htmlLink);
},
};
}Handle PWA info and assets in SSR environments:
// Check availability before use
function getSSRPWAInfo() {
try {
const info = pwaInfo;
return info || null;
} catch {
// Module not available in SSR
return null;
}
}
// Conditional asset injection
function injectPWAAssetsSSR() {
try {
const assets = pwaAssetsHead;
return assets.links.map(link =>
`<link rel="${link.rel}" href="${link.href}"${
link.sizes ? ` sizes="${link.sizes}"` : ''
}${link.type ? ` type="${link.type}"` : ''}${
link.media ? ` media="${link.media}"` : ''
}>`
).join('\n');
} catch {
return '';
}
}Integration patterns for popular meta-frameworks:
// Next.js Head component integration
import Head from "next/head";
import { pwaAssetsHead } from "virtual:pwa-assets/head";
function PWAHead() {
return (
<Head>
{pwaAssetsHead.links.map((link, index) => (
<link
key={index}
rel={link.rel}
href={link.href}
sizes={link.sizes}
type={link.type}
media={link.media}
/>
))}
{pwaAssetsHead.themeColor && (
<meta
name={pwaAssetsHead.themeColor.name}
content={pwaAssetsHead.themeColor.content}
/>
)}
</Head>
);
}
// Nuxt.js useHead integration
import { useHead } from "@vueuse/head";
import { pwaAssetsHead } from "virtual:pwa-assets/head";
function usePWAHead() {
useHead({
link: pwaAssetsHead.links,
meta: pwaAssetsHead.themeColor ? [pwaAssetsHead.themeColor] : [],
});
}Load PWA assets dynamically based on conditions:
import { pwaAssetsIcons } from "virtual:pwa-assets/icons";
async function loadOptimalIcon(preferredSize = "192x192") {
// Try to find icon with preferred size
const transparent = Object.values(pwaAssetsIcons.transparent);
let icon = transparent.find(
(icon) => `${icon.size.width}x${icon.size.height}` === preferredSize
);
// Fall back to first available icon
if (!icon && transparent.length > 0) {
icon = transparent[0];
}
if (icon) {
// Preload the icon
const link = document.createElement("link");
link.rel = "preload";
link.as = "image";
link.href = icon.src;
document.head.appendChild(link);
return icon;
}
return null;
}Install with Tessl CLI
npx tessl i tessl/npm-vite-plugin-pwa