add pwa in nextjs

This commit is contained in:
Aman Varshney
2025-05-21 13:02:58 +05:30
parent cd8b5a9db4
commit 01d745a3e5
15 changed files with 83 additions and 11 deletions

View File

@@ -0,0 +1,5 @@
---
"create-better-t-stack": minor
---
add pwa support in nextjs

View File

@@ -10,7 +10,9 @@ import type { ProjectConfig } from "../types";
export async function setupAddons(config: ProjectConfig) { export async function setupAddons(config: ProjectConfig) {
const { addons, frontend, projectDir } = config; const { addons, frontend, projectDir } = config;
const hasReactWebFrontend = const hasReactWebFrontend =
frontend.includes("react-router") || frontend.includes("tanstack-router"); frontend.includes("react-router") ||
frontend.includes("tanstack-router") ||
frontend.includes("next");
const hasNuxtFrontend = frontend.includes("nuxt"); const hasNuxtFrontend = frontend.includes("nuxt");
const hasSvelteFrontend = frontend.includes("svelte"); const hasSvelteFrontend = frontend.includes("svelte");
const hasSolidFrontend = frontend.includes("solid"); const hasSolidFrontend = frontend.includes("solid");

View File

@@ -557,10 +557,20 @@ export async function setupAddonsTemplate(
let addonDestDir = projectDir; let addonDestDir = projectDir;
if (addon === "pwa") { if (addon === "pwa") {
addonSrcDir = path.join(PKG_ROOT, "templates/addons/pwa/apps/web"); const webAppDir = path.join(projectDir, "apps/web");
addonDestDir = path.join(projectDir, "apps/web"); if (!(await fs.pathExists(webAppDir))) {
continue;
if (!(await fs.pathExists(addonDestDir))) { }
addonDestDir = webAppDir;
if (context.frontend.includes("next")) {
addonSrcDir = path.join(PKG_ROOT, "templates/addons/pwa/apps/web/next");
} else if (
context.frontend.some((f) =>
["tanstack-router", "react-router", "solid"].includes(f),
)
) {
addonSrcDir = path.join(PKG_ROOT, "templates/addons/pwa/apps/web/vite");
} else {
continue; continue;
} }
} }

View File

@@ -800,7 +800,10 @@ function processAndValidateFlags(
); );
const hasCompatibleWebFrontend = effectiveFrontend?.some((f) => { const hasCompatibleWebFrontend = effectiveFrontend?.some((f) => {
const isPwaCompatible = const isPwaCompatible =
f === "tanstack-router" || f === "react-router" || f === "solid"; f === "tanstack-router" ||
f === "react-router" ||
f === "solid" ||
f === "next";
const isTauriCompatible = const isTauriCompatible =
f === "tanstack-router" || f === "tanstack-router" ||
f === "react-router" || f === "react-router" ||
@@ -828,7 +831,7 @@ function processAndValidateFlags(
let incompatibleReason = "Selected frontend is not compatible."; let incompatibleReason = "Selected frontend is not compatible.";
if (config.addons.includes("pwa")) { if (config.addons.includes("pwa")) {
incompatibleReason = incompatibleReason =
"PWA requires tanstack-router, react-router, or solid."; "PWA requires tanstack-router, react-router, next, or solid.";
} }
if (config.addons.includes("tauri")) { if (config.addons.includes("tauri")) {
incompatibleReason = incompatibleReason =

View File

@@ -18,7 +18,8 @@ export async function getAddonsChoice(
const hasCompatiblePwaFrontend = const hasCompatiblePwaFrontend =
frontends?.includes("react-router") || frontends?.includes("react-router") ||
frontends?.includes("tanstack-router") || frontends?.includes("tanstack-router") ||
frontends?.includes("solid"); frontends?.includes("solid") ||
frontends?.includes("next");
const hasCompatibleTauriFrontend = const hasCompatibleTauriFrontend =
frontends?.includes("react-router") || frontends?.includes("react-router") ||

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="92" height="92"><svg width="92" height="92" viewBox="0 0 92 92" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="8" y="8" width="76" height="76" rx="12" fill="#F5EEFF" stroke="#B79AFF" stroke-width="3"></rect>
<text x="46" y="56" text-anchor="middle" font-family="monospace" font-size="40" fill="#8F5BFF">$<tspan dx="0" dy="0">_</tspan></text>
</svg><style>@media (prefers-color-scheme: light) { :root { filter: none; } }
@media (prefers-color-scheme: dark) { :root { filter: none; } }
</style></svg>

After

Width:  |  Height:  |  Size: 616 B

View File

@@ -0,0 +1,21 @@
{
"name": "{{projectName}}",
"short_name": "{{projectName}}",
"icons": [
{
"src": "/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,26 @@
import type { MetadataRoute } from "next";
export default function manifest(): MetadataRoute.Manifest {
return {
name: "{{projectName}}",
short_name: "{{projectName}}",
description:
"my pwa app",
start_url: "/new",
display: "standalone",
background_color: "#ffffff",
theme_color: "#000000",
icons: [
{
src: "/favicon/web-app-manifest-192x192.png",
sizes: "192x192",
type: "image/png",
},
{
src: "/favicon/web-app-manifest-512x512.png",
sizes: "512x512",
type: "image/png",
},
],
};
}

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,7 +1,5 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {};
output: "export"
};
export default nextConfig; export default nextConfig;