Skip to content

feat: setup SSR rendering for improved SEO#619

Open
isolkewo wants to merge 2 commits intov0l:mainfrom
isolkewo:openhands/issue-616-ssr-render-posts
Open

feat: setup SSR rendering for improved SEO#619
isolkewo wants to merge 2 commits intov0l:mainfrom
isolkewo:openhands/issue-616-ssr-render-posts

Conversation

@isolkewo
Copy link
Copy Markdown
Contributor

This PR sets up Server-Side Rendering (SSR) for the Snort application to improve SEO by rendering threads and profiles from the server side.

Changes made

  • Added entry-server.tsx - SSR entry point using renderToString and StaticRouter
  • Added server.ts - Express-based SSR server implementation
  • Updated vite.config.ts - Added SSR mode detection and separate build outputs
  • Updated package.json - Added build:client, build:ssr, and server scripts
  • Added express and @types/express dependencies
  • Updated Dockerfile - Changed from nginx to Express SSR server
  • Added SSR_SETUP.md - Documentation for SSR setup and deployment

How it works

  1. Client build: Creates the standard client-side bundle in build/client/
  2. Server build: Creates the SSR bundle in build/server/
  3. SSR server: Handles requests by rendering React to HTML on the server and injecting into the template

Benefits

  • Improved SEO: Search engines can crawl fully rendered HTML
  • Faster first paint: Content available immediately without waiting for JavaScript
  • Better social sharing: Open Graph and Twitter Card meta tags properly rendered
  • Enhanced UX: Users see content faster on slow connections

Deployment

# Build both client and server
bun run build

# Start SSR server
bun run server

# Or using Docker
docker build -t snort-ssr .
docker run -p 3000:3000 snort-ssr

Closes #616

- Add entry-server.tsx for server-side rendering entry point
- Add server.ts with Express SSR server implementation
- Update vite.config.ts to support SSR build mode
- Add build:client, build:ssr, and server scripts to package.json
- Add express and @types/express dependencies
- Update Dockerfile to use SSR server instead of nginx
- Add SSR_SETUP.md documentation

This enables SEO-friendly rendering of threads and profiles by
pre-rendering pages on the server before sending to the client.

Closes v0l#616
Copy link
Copy Markdown
Owner

@v0l v0l left a comment

Choose a reason for hiding this comment

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

Use Bun for prod server, make sure to setup some data loading with correct hydration path using react router

Take a look at https://github.com/LNVPS/web for reference

Comment thread packages/app/server.ts Outdated
import path from "node:path"
import { fileURLToPath } from "node:url"

import express from "express"
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

dont use express use bun for prod

- Replace Express server with Bun.serve() for production
- Add server/prod.ts for high-performance Bun-based serving
- Add server/ssr-render.ts for shared SSR render logic
- Update entry-server.tsx to use React Router's createStaticHandler
  for proper data loading with route loaders
- Add entry-client.tsx for client-side hydration with SSR locale/messages
- Update index.html with SSR placeholders (<!--ssr-cache-->)
- Add server:prod script for production serving with Bun
- Proper hydration path: locale and messages passed via window.__SSR_LOCALE__
  and window.__SSR_MESSAGES__ for correct client hydration

This follows the reference implementation pattern for improved SEO and
faster initial page loads.
@isolkewo
Copy link
Copy Markdown
Contributor Author

Hey @v0l! 👋

I've updated the SSR setup with Bun for the production server and proper React Router data loading with correct hydration. Here's what was done:

Changes made

1. Bun Production Server (packages/app/server/prod.ts)

  • Replaced Express-based server with Bun.serve() for better performance
  • Uses the Bun runtime natively without Node.js overhead

2. React Router Data Loading (packages/app/src/entry-server.tsx)

  • Updated to use createStaticHandler and createStaticRouter from React Router
  • Route loaders are now automatically invoked during SSR
  • Proper data loading pattern that hydrates correctly on the client

3. Hydration Path (packages/app/src/entry-client.tsx)

  • Added entry-client.tsx for client-side hydration
  • SSR locale and messages are passed via window.__SSR_LOCALE__ and window.__SSR_MESSAGES__
  • Client reads these values to hydrate with matching state, preventing hydration mismatches

4. Shared SSR Logic (packages/app/server/ssr-render.ts)

  • Created a reusable renderPage function that handles SSR rendering
  • Accepts Accept-Language and Cookie headers for locale detection

5. Updated Build Scripts (packages/app/package.json)

  • Added server:prod script for production serving with Bun
  • Updated server script to use the new Bun-based server

The implementation follows the reference pattern you mentioned, with proper locale handling, data loading through React Router loaders, and correct hydration for SEO-friendly rendering.

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.

SSR render posts

4 participants