Skip to content

Pre-render site to static HTML at build time (SSG) #39

@andimrob

Description

@andimrob

Problem

The site ships an empty <div id="root"></div> — all content is rendered client-side by JavaScript. Crawlers that don't execute JS (Bing, social media scrapers, AI indexers) see no content. Googlebot does execute JS but indexes it on a slower second pass, making it less reliable.

Recommendation

Pre-render the React app to static HTML at build time. Since this is a portfolio with no dynamic data, the entire page can be rendered once during npm run build and shipped as fully-formed HTML that hydrates into the interactive SPA on load.

Approach

Add a simple prerender script that:

  1. Imports the App component in a Node/SSR context
  2. Calls renderToString(<App />) from react-dom/server
  3. Injects the resulting HTML into the <div id="root"> in the built index.html
  4. Runs as a post-build step

The entry point (main.tsx) would switch from createRoot + render to hydrateRoot so React attaches to the existing DOM rather than replacing it. All interactive features (X-ray, prism flip, confetti, cursor glow) continue to work after hydration.

Considerations

  • The imperative DOM modules (xray.ts, confetti.ts, coinCollect.ts) don't run during SSR — they only attach listeners on mount, so no changes needed
  • useTypewriter starts with an empty string and animates in — this is fine, the SSR output just shows the initial empty state and the animation plays on hydrate
  • The window/document references in hooks need to be guarded or only run in useEffect (which doesn't fire during SSR) — audit for any top-level browser API access
  • Alternatively, consider vite-plugin-prerender or vite-ssg instead of a custom script

Depends on

This is independent of but complementary to adding meta tags to <head>. Meta tags handle social previews and search metadata; SSG handles content indexing.

Priority

Medium — the correct architectural fix for SEO, but lower urgency for a portfolio site that's primarily shared via direct links.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions