Skip to content

reduce the time of prerender screen for better seo and crawbility#42

Open
krishnnag998-del wants to merge 2 commits into
hrx01-dev:mainfrom
krishnnag998-del:vite-plugin-ssg
Open

reduce the time of prerender screen for better seo and crawbility#42
krishnnag998-del wants to merge 2 commits into
hrx01-dev:mainfrom
krishnnag998-del:vite-plugin-ssg

Conversation

@krishnnag998-del

@krishnnag998-del krishnnag998-del commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

DESCRIPTION

close #21
This issue is about SEO and initial page loading, not about a bug in your React code.
BENIFITS
The benefits of implementing are:

-- Better SEO – Search engines can read your page content, improving search rankings.
-- Faster Initial Load – Users see page content sooner because HTML is already generated.
-- Better Social Media Previews – Links shared on WhatsApp, LinkedIn, X, Facebook, etc., show proper titles, descriptions, and images.
-- Improved User Experience – Reduces the blank screen users see while JavaScript loads.
-- Minimal Code Changes – Only a few configuration files need updates; existing React components usually stay unchanged.
-- No Change in Functionality – The application's features and behavior remain the same.
-- Production Ready – Makes the application more optimized for deployment and easier for search engine crawlers to index.
((Puppeteer is a headless browser that opens your website, waits for React to load, and then saves the fully rendered HTML. This pre-rendered HTML is served to search engines and users, which helps reduce the initial blank screen, improves SEO, and creates better social media previews. It requires only minimal changes to the existing React + Vite application without changing its functionality.))

Summary by CodeRabbit

Release Notes

  • New Features

    • Enabled static page pre-rendering to improve performance and SEO by generating optimized output during the build.
  • Chores

    • Updated the build process to run pre-rendering after the Vite build completes.
    • Added browser automation tooling to support pre-rendering.
    • Cleaned up duplicate dependency entries and minor project formatting.
  • Bug Fixes

    • Improved app startup behavior by safely initializing the root element before rendering.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ab435208-fad0-4f07-93a1-cf2d44335726

📥 Commits

Reviewing files that changed from the base of the PR and between cd717ad and 1d460fd.

📒 Files selected for processing (1)
  • scripts/prerender.js

📝 Walkthrough

Walkthrough

Adds a Puppeteer-based post-build pre-rendering step to fix the blank-page issue for crawlers. A new scripts/prerender.js serves the Vite dist/ output, navigates configured routes, captures rendered HTML, and writes it back to dist/. The build script is updated to run this script after vite build, puppeteer is added as a devDependency, and src/main.tsx is guarded to mount React only when the #root element is present.

Changes

Static Pre-Rendering Pipeline

Layer / File(s) Summary
Build script wiring, dependency, and React root guard
package.json, src/main.tsx
scripts.build is updated to run node scripts/prerender.js after vite build; puppeteer ^25.1.0 is added to devDependencies; a duplicate @radix-ui/react-toggle-group dependency entry is removed. src/main.tsx stores the #root element in a variable and conditionally mounts the React tree only when the element exists, replacing the non-null assertion.
Prerender script: config constants and static file server
scripts/prerender.js
Defines the dist path, server port, and target routes as configuration constants. Implements startServer(port) — an HTTP server that maps / and /404.html to their HTML files, resolves other paths under dist/, sets Content-Type by extension, and resolves with the listening server instance.
Prerender orchestration: Puppeteer navigation, HTML capture, and cleanup
scripts/prerender.js
prerender() validates dist/ exists, starts the static server, launches Puppeteer, and for each configured route navigates to the local URL with networkidle2, captures page.content(), ensures the output directory exists, and writes the HTML to dist/. Per-route errors are caught and logged. A finally block closes the browser and server; fatal errors call process.exit(1).

Sequence Diagram(s)

sequenceDiagram
  actor BuildPipeline as npm run build
  participant ViteBuild as vite build
  participant PrerenderScript as scripts/prerender.js
  participant HTTPServer as Static HTTP Server
  participant PuppeteerBrowser as Puppeteer
  participant DistFS as dist/ filesystem

  BuildPipeline->>ViteBuild: vite build
  ViteBuild->>DistFS: write bundled assets
  ViteBuild-->>BuildPipeline: exit 0
  BuildPipeline->>PrerenderScript: node scripts/prerender.js
  PrerenderScript->>DistFS: validate dist/ exists
  PrerenderScript->>HTTPServer: startServer(port)
  HTTPServer-->>PrerenderScript: listening server
  PrerenderScript->>PuppeteerBrowser: launch()
  loop each configured route
    PrerenderScript->>PuppeteerBrowser: page.goto(localhost/route, networkidle2)
    PuppeteerBrowser-->>PrerenderScript: page.content() HTML
    PrerenderScript->>DistFS: write HTML to dist/<route>/index.html
  end
  PrerenderScript->>PuppeteerBrowser: browser.close()
  PrerenderScript->>HTTPServer: server.close()
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 Hop hop, the crawler came to see,
A blank white page — oh dear, oh me!
Now Puppeteer sneaks through each route,
Captures the HTML, writes it out.
Static HTML, fresh from the warren,
No more blank pages — content is flowin'! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title mentions 'prerender screen' and 'better seo and crawbility' which directly align with the PR's core objective of implementing pre-rendering for improved SEO and crawler accessibility.
Linked Issues check ✅ Passed The PR implements Puppeteer-based pre-rendering that generates static HTML with full page content, satisfying both acceptance criteria: landing page pre-rendering and verified content presence via raw HTML.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing pre-rendering: package.json updates (Puppeteer dependency and build script), prerender.js script for static HTML generation, and src/main.tsx improvements for safer root element handling.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch vite-plugin-ssg

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
scripts/prerender.js (1)

18-18: ⚡ Quick win

Use an ephemeral port and explicit listen error handling.

Line 18 hardcodes 3000; if it’s occupied, this step is flaky. Also, startServer should reject on server listen errors instead of relying on implicit process-level behavior. Prefer 0 (OS-assigned port) and build URLs from the actual bound port.

🔧 Suggested refactor
-const PORT = 3000;
+const PORT = Number(process.env.PRERENDER_PORT ?? 0);

 function startServer(port) {
-  return new Promise((resolve) => {
+  return new Promise((resolve, reject) => {
@@
-    server.listen(port, () => {
-      console.log(`[Prerender] Server started on http://localhost:${port}`);
-      resolve(server);
+    server.on('error', reject);
+    server.listen(port, '127.0.0.1', () => {
+      const address = server.address();
+      const actualPort =
+        typeof address === 'object' && address ? address.port : port;
+      console.log(`[Prerender] Server started on http://127.0.0.1:${actualPort}`);
+      resolve({ server, port: actualPort });
     });
   });
 }
@@
-  let server;
+  let server;
+  let serverPort;
@@
-    server = await startServer(PORT);
+    ({ server, port: serverPort } = await startServer(PORT));
@@
-      const url = `http://localhost:${PORT}${route}`;
+      const url = `http://127.0.0.1:${serverPort}${route}`;

Also applies to: 23-23, 56-59, 76-76, 90-90

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/prerender.js` at line 18, Replace the hardcoded PORT constant value
of 3000 with 0 to use an OS-assigned ephemeral port, preventing flakiness when
the port is already occupied. Update the startServer function to explicitly
handle and reject on server listen errors instead of relying on implicit
process-level behavior. Finally, modify all references to PORT (on lines 23,
56-59, 76, and 90) to dynamically use the actual bound port by calling
server.address().port after the server successfully listens, ensuring URLs are
constructed with the correct port regardless of which ephemeral port was
assigned.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/prerender.js`:
- Around line 24-29: The http.createServer callback constructs filePath directly
from req.url without validation, creating a directory traversal vulnerability.
Normalize the URL pathname using path.normalize() or similar, then resolve the
complete file path and verify that it remains within DIST_DIR (e.g., by checking
that path.resolve(filePath).startsWith(path.resolve(DIST_DIR))). Only proceed
with file operations like fs.readFile if the resolved path passes this
validation check, preventing requests with traversal segments like /../../../
from accessing files outside the dist directory.
- Around line 117-123: In the prerender.js script, the route pre-rendering loop
catches and logs errors for failed routes but then unconditionally reports
success and exits with code 0. To fix this, create an array to track failed
routes and push route names to it whenever an error is caught in the catch
block. After the try-catch loop completes, check if the failed routes array has
any entries, and if it does, throw an error with details about which routes
failed instead of logging the success message. This ensures the process will
exit with a non-zero code when any route pre-rendering fails, preventing broken
deployments from appearing healthy.

---

Nitpick comments:
In `@scripts/prerender.js`:
- Line 18: Replace the hardcoded PORT constant value of 3000 with 0 to use an
OS-assigned ephemeral port, preventing flakiness when the port is already
occupied. Update the startServer function to explicitly handle and reject on
server listen errors instead of relying on implicit process-level behavior.
Finally, modify all references to PORT (on lines 23, 56-59, 76, and 90) to
dynamically use the actual bound port by calling server.address().port after the
server successfully listens, ensuring URLs are constructed with the correct port
regardless of which ephemeral port was assigned.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6a5eb42-e641-4ed9-b0c4-1f9ab0a8ba93

📥 Commits

Reviewing files that changed from the base of the PR and between 8ed0118 and cd717ad.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • package.json
  • scripts/prerender.js
  • src/main.tsx

Comment thread scripts/prerender.js
Comment thread scripts/prerender.js
@hrx01-dev

Copy link
Copy Markdown
Owner

@krishnnag998-del THIS BRANCH HAS MERGE CONFLICTS , RESOLVE IT FOR THIS PR TO BE MERGED

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.

Site is client-rendered only — crawlers & social scrapers get a blank page

2 participants