Skip to content

fix: blank page when javascript is disabled#1426

Merged
danielroe merged 8 commits intonpmx-dev:mainfrom
NandkishorJadoun:fix/blank-page-disable-js
Feb 12, 2026
Merged

fix: blank page when javascript is disabled#1426
danielroe merged 8 commits intonpmx-dev:mainfrom
NandkishorJadoun:fix/blank-page-disable-js

Conversation

@NandkishorJadoun
Copy link
Contributor

@NandkishorJadoun NandkishorJadoun commented Feb 12, 2026

resolves #1291

Description

This pr adds a nitro server plugin that injects a <meta http-equiv="refresh"> tag specifically into the SPA fallback shell (200.html), wrapped in a <noscript> block.

@vercel
Copy link

vercel bot commented Feb 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Feb 12, 2026 4:10pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Feb 12, 2026 4:10pm
npmx-lunaria Ignored Ignored Feb 12, 2026 4:10pm

Request Review

@codecov
Copy link

codecov bot commented Feb 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@NandkishorJadoun
Copy link
Contributor Author

@danielroe , should i reduce the delay to 0 seconds? im concerned that might trigger an infinite loop, though I'm not entirely sure. i've set it to 1 second for now to be safe

@ghostdevv ghostdevv requested a review from danielroe February 12, 2026 15:46
@ghostdevv
Copy link
Contributor

cc: @lorypelli

@danielroe
Copy link
Member

danielroe commented Feb 12, 2026

we may need to disable the validator on 200.html

on the other hand, I think setting to 0 should be safe.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

Adds a Nitro plugin that registers a render:html hook to modify HTML output during prerendering. When prerender is active and the request path is /200.html, the hook injects a noscript meta refresh tag into the document head. The change also updates Nuxt configuration to disable the HTML validator rule for meta-refresh.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description clearly relates to the changeset, explaining the addition of a Nitro plugin for injecting a meta refresh tag into the SPA fallback shell.
Linked Issues check ✅ Passed The changes implement the core objective from issue #1291 by adding a Nitro plugin that injects a meta refresh tag into 200.html, restoring SPA fallback functionality for users with JavaScript disabled.
Out of Scope Changes check ✅ Passed All changes are within scope: the Nitro plugin addresses the JavaScript-disabled scenario, and the htmlValidator configuration change supports the meta refresh injection by disabling conflicting validation rules.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Comment on lines 2 to 4
nitroApp.hooks.hook('render:html', (html, { event }) => {
if (event.path.includes('/200.html')) {
html.head.push('<noscript><meta http-equiv="refresh" content="0"></noscript>')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n "200\.html|render:html|spa|fallback" -S

Repository: npmx-dev/npmx.dev

Length of output: 50374


🏁 Script executed:

# Find the fallback-ui.ts file
find . -name "fallback-ui.ts" -type f

# Look for nitro config and SPA setup
find . -name "nuxt.config.*" -o -name "nitro.config.*" | head -20

# Search for 200.html references specifically
rg "200\.html" --type ts --type js -B 2 -A 2

Repository: npmx-dev/npmx.dev

Length of output: 1469


🏁 Script executed:

# Read the fallback-ui.ts file completely
cat -n server/plugins/fallback-ui.ts

# Read the isr-fallback.ts module to understand SPA fallback handling
cat -n modules/isr-fallback.ts

# Check nuxt.config.ts for SPA/fallback configuration
rg -A 10 -B 5 "ssr:|fallback:|spa:" nuxt.config.ts

Repository: npmx-dev/npmx.dev

Length of output: 3925


🏁 Script executed:

# Check how Nitro handles the render:html hook during fallback serving
rg -B 5 -A 5 "render:html" server/

# Look for event.path usage in other plugins/middleware to understand what value it has
rg "event\.path" server/ -B 2 -A 2

Repository: npmx-dev/npmx.dev

Length of output: 1753


🏁 Script executed:

# Check if there's any rewrite/redirect logic that might change event.path
rg -B 3 -A 3 "spa\.prerender-fallback" 

# Look for other render:html hooks that might provide insight into how fallback rendering works
rg -B 5 -A 5 "prerender-fallback"

Repository: npmx-dev/npmx.dev

Length of output: 2118


Confirm fallback detection matches Nitro's SPA behaviour – the guard condition is ineffective for dynamic routes.

The check event.path.includes('/200.html') will only match direct requests to /200.html. When Nitro serves the SPA fallback for routes like /package/:id (via spa.prerender-fallback.html), event.path retains the original requested path (e.g. /package/lodash), not /200.html. The noscript refresh injection will never run for fallback requests to dynamic routes. Either check for specific fallback routes directly (e.g. event.path.startsWith('/package/')) or use a different mechanism to detect fallback serving.

Comment on lines 3 to 4
if (event.path.includes('/200.html')) {
html.head.push('<noscript><meta http-equiv="refresh" content="0"></noscript>')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid an infinite no‑JS refresh loop.
A meta refresh without a URL reloads the same document; with content="0" this becomes a tight loop for users without JS and effectively blocks interaction. Add a URL target and a one‑time guard to break the cycle.

🛠️ Proposed fix to break the refresh loop
 export default defineNitroPlugin(nitroApp => {
   nitroApp.hooks.hook('render:html', (html, { event }) => {
-    if (event.path.includes('/200.html')) {
-      html.head.push('<noscript><meta http-equiv="refresh" content="0"></noscript>')
-    }
+    const requestUrl = event.node.req.url ?? event.path
+    const pathOnly = requestUrl.split('?')[0]
+    const isFallback = pathOnly.endsWith('/200.html')
+    const hasNoJsGuard = requestUrl.includes('__nojs=1')
+    if (isFallback && !hasNoJsGuard) {
+      const separator = requestUrl.includes('?') ? '&' : '?'
+      const target = `${requestUrl}${separator}__nojs=1`
+      html.head.push(`<noscript><meta http-equiv="refresh" content="0;url=${target}"></noscript>`)
+    }
   })
 })

@danielroe danielroe added this pull request to the merge queue Feb 12, 2026
Merged via the queue into npmx-dev:main with commit 4f4bc1c Feb 12, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

package page doesn't load with javascript disabled

3 participants