ToolActToolAct

Demo

Tool demo page

DataSSR
rettrue
msgnull
statusCode0
dataHello World

What is SSR?

This demo page is a small server-side rendering reference route rather than an end-user utility. It shows how a Next.js App Router page can fetch data on the server, render the result into the first HTML response, and still load localized labels for the current locale. That makes it a compact check for shared infrastructure wiring: environment variables, signed server fetches, API response shape, locale routing, and translation loading. When this page works, it proves the basic SSR path is healthy before debugging a more complex tool page. When it fails, the issue is likely in common server configuration or API connectivity. It should be kept simple so it remains a reliable smoke test for deployment and framework changes.

How to Use

How to use

  1. The page automatically calls the back-end API on the server
  2. HTML includes pre-rendered content, no client request needed
  3. View the data returned by the API
  4. Ideal for pages that need SEO optimization

Development Notes

  • Use this page as a smoke test for SSR, locale loading, signed server requests, and API connectivity.
  • If this page fails, check shared server configuration before debugging a specific tool page.

Use Cases

Verify the signed server-side API pathOpen the demo page to confirm SSR data can be fetched through serverPageFetch with the current locale and API_BASE_URL configuration. The page is force-dynamic, so the rendered ret, msg, statusCode, and data fields always reflect the most recent server response, which is what you want for a smoke test. This is a debug harness, not a user feature: a healthy response on this route means the shared signing key, nonce store, and locale negotiation are all in good shape before a real tool is debugged.
Check how backend response fields render in the app shellThe page displays ret, msg, statusCode, and data with an SSR badge, which gives developers a compact smoke test for response shape and error-state presentation. Because the page is rendered on the server, you can confirm that the i18n message loading, the locale segment, and the signed-fetch wrapper all work without manually instrumenting the client. Comparing the ret code and the rendered error title side by side also helps verify that error mapping is wired up correctly, so a real user-facing page will show translated errors instead of raw API strings.
Use it as an internal integration referenceBecause it is force-dynamic and server-rendered, this page is a compact example for future tools that need signed server API access rather than purely client-side processing. Use it as a working reference when adding another SSR page that calls the backend. Keep the demo's surface area minimal on purpose: a small, predictable response shape is what makes the smoke test trustworthy, while a sprawling demo would obscure which subsystem actually broke.
Catch locale routing regressions with this SSR checkVisit the demo under several locale prefixes to confirm that localized labels and the ret/msg/data fields still render server-side, exposing routing or translation-loader issues early. A missing translation key or an off-by-one segment under one locale usually shows up here before any user-facing tool is tested. When the demo breaks under one locale but passes under another, the regression is almost always in the locale segment, the message loader, or the routing tree, not in the signed-fetch wrapper itself.
Verify X-Timestamp, X-Nonce, and X-Sign headers in the wire logInspect the outgoing request from the demo page to confirm the signed server fetch includes the expected headers, which is the fastest way to debug 401/403 errors from the real API. The same headers also surface in browser DevTools when proxying through the local dev server, so the signed-fetch wrapper can be verified end to end. If the signature looks correct but the upstream still rejects, the clock skew on the server (X-Timestamp) or a nonce collision (X-Nonce) is usually to blame, and the demo's request log is the cleanest place to confirm both before opening a backend ticket.

Technical Principle

This Demo page is built on Next.js 16 App Router and uses server-side rendering (SSR) by default. When a user requests the page, the Node.js server first runs the React component, calls the back-end API to get data, then returns the fully rendered HTML to the browser in a single response. The browser starts parsing and painting the HTML right away, so users see the complete content in the first frame without waiting for JavaScript to download and execute. This stands in sharp contrast to traditional client-side rendering (CSR). Under CSR, the server only returns a near-empty HTML shell and a JS link; the browser has to download the JS bundle, run framework initialization, call APIs to fetch data, and only then render the page. From the user's perspective this is 'a blank screen for a while', and it is even worse for search engine crawlers - most crawlers do not execute JavaScript and only see the empty shell. After SSR, Next.js also injects a chunk of JavaScript to perform 'hydration': associating the existing DOM with the React component tree and binding event listeners so the page becomes interactive. The full flow is HTML parse -> CSS load -> JS download -> hydrate -> interactive. Among the three Core Web Vitals, LCP (Largest Contentful Paint) and CLS (Cumulative Layout Shift) are usually better under SSR, while INP (Interaction to Next Paint) depends on hydration speed and event-handler implementation quality.

  • Next.js App Router: pages in the App Router are Server Components by default, running on the server and returning HTML automatically with no extra configuration.
  • SSR vs CSR: SSR delivers fast first paint and SEO friendliness at the cost of server resources; CSR fits internal dashboards and other interactive scenarios that do not need SEO.
  • First paint flow: HTML parse -> CSS load and render -> JS download and execute -> React hydrate -> page interactive; each step affects LCP.
  • SEO crawl friendliness: mainstream crawlers like Googlebot and Bingbot read the HTML text directly; SSR output of complete content guarantees crawl coverage.
  • Web Vitals thresholds: LCP < 2.5s, INP < 200ms, CLS < 0.1 are Google's recommended 'good' thresholds; SSR makes it easier to hit LCP and CLS.
  • Signed requests: the Demo page calls the back-end API through serverApiFetch on the server, automatically carrying X-Timestamp, X-Nonce, and X-Sign to complete authentication.

Examples

SSR (Server-Side Rendering)

// app/page.tsx (default Server Component)
export default async function Page() {
  const data = await fetch('/api/info').then(r => r.json());
  return <div>{data.title}</div>;
}
// The HTML already directly contains <div>actual content</div>

CSR (Client-Side Rendering)

'use client';
import { useEffect, useState } from 'react';

export default function Page() {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetch('/api/info').then(r => r.json()).then(setData);
  }, []);
  return <div>{data?.title || 'Loading...'}</div>;
}
// The first-paint HTML only contains <div>Loading...</div>

First-Paint Speed Comparison

# SSR
FCP: 0.4s  LCP: 0.8s  TTI: 1.2s
SEO: crawlers get the complete content directly

# CSR
FCP: 0.6s  LCP: 1.8s  TTI: 2.4s
SEO: crawlers must execute JS to get the content

FAQ

What is this demo page actually demonstrating?

It is a server-side rendering (SSR) reference route that shows how a Next.js App Router page can fetch data on the server, embed the result in the first HTML response, and still load the correct localized labels. It is not an end-user utility - it is a smoke test for developers.

Is the data fetched on the server or in the browser?

On the server. The page calls serverApiFetch (with the signed-request headers from src/lib/sign.ts) inside the React Server Component, so the browser receives an HTML page that already contains the data. There is no client-side fetch on first load.

Why does the page sometimes show stale data?

Next.js may cache the SSR response depending on revalidate settings. If you need fresh data on every request, set 'export const dynamic = "force-dynamic"' or pass {cache: 'no-store'} to the fetch. The demo route uses default behaviour to keep the example simple.

Can I copy this pattern for my own page?

Yes - that is the point. The route demonstrates how to combine getTranslations from next-intl with a server-side data fetch and pass the result to a Client Component for interactivity. Copy layout.tsx and page.tsx and replace the data source.

Why do I need request signing for an internal API?

X-Timestamp / X-Nonce / X-Sign block replay attacks and ensure that requests from the public web actually originate from a trusted client. The signing key is per-session for logged-in users and a default value otherwise; see src/lib/sign.ts and src/lib/fetch.ts for the implementation.

Will this page exist in production builds?

It is part of the deployed app for reference, but it is not linked from the home page or tools list and is not advertised as a user-facing tool. Treat it as developer documentation rendered as a route.

What should I look at if I want to learn the project layout?

Start with this page's layout.tsx and page.tsx, then read src/i18n.ts (locale config), src/i18n/request.ts (global translation loading), src/lib/load-page-messages.ts (per-page translations), and src/lib/fetch.ts (signed-request wrappers).