Free tools Windows power users keep installed
One-click scans. No signup required.
In Piyush Chauhan’s hotel-and-editorial site, ASP.NET Core Razor Pages owns public routes, content, metadata, and the HTML shell; React islands add selected interactions; and a separate Node/Playwright worker renders those islands before publication. The public gateway then serves the completed HTML from PostgreSQL, rather than launching Chromium for each visitor. Chauhan describes the approach as browser rendering at publication time—not React server-side rendering in .NET.
What the architecture does
Chauhan’s implementation separates the public document from the interactive features embedded in it. Razor Pages generates the page and its useful no-JavaScript content. React handles selected controls, including search and date controls, a gallery, mobile navigation, and inquiry-form interaction. PostgreSQL holds catalog content, snapshot jobs, and published HTML. A Node worker uses Playwright and Chromium to produce and validate the finished pages, while the public gateway serves stored documents.
As an Amazon Associate I earn from qualifying purchases.
The site is a hotel-and-editorial site with catalog pages, journal content, search, and a stay inquiry flow; it is not described as a reservation or payment system. Its admin interface is a separate, protected React Router single-page application.
Recommended Free Tools
How a page moves from edit to visitor
- Razor generates the source page. The page includes content, metadata, island markers, and serialized props—for example, a gallery marker populated with hotel gallery data.
- A change queues affected routes. An edit or release creates snapshot jobs for the relevant canonical paths.
- The worker requests an internal source page. It asks a token-protected Razor route for an allowed canonical path. For capture, islands mount eagerly, even if a visitor-facing runtime would normally defer a visible island.
- Playwright waits and captures. The browser waits for the page’s readiness signal and execution to finish, then serializes the document.
- The worker validates before publishing. It checks document invariants and whether the job is still current. If valid and current, it stores the completed HTML atomically with job completion; if a newer version superseded the job, it discards the capture.
- The gateway serves the saved document. React hydrates populated island markers. Empty markers in a live development context can instead be rendered from scratch.
Chauhan also describes removing Chromium-added Vite modulepreload hints and inserting separators between adjacent island text nodes so the saved HTML hydrates correctly. On visitor requests, an IntersectionObserver can defer visible islands; the capture runtime mounts all islands eagerly.
#1 Best Overall
Which routes are snapshots—and which stay live
The implementation’s boundary is based on whether a response is canonical and public, not simply whether a page can be rendered. Canonical public GET or HEAD routes are snapshot candidates. User-specific, filtered, administrative, and API traffic remains live and private.
- Public candidates: canonical hotel, destination, article, home, offer, and bare search pages.
- Live/private: filtered search results, inquiry pages and submissions, admin documents, and APIs. Filtered results use
private, no-storeandnoindex,follow.
For example, bare /search can be a canonical snapshot, while query-specific results are excluded from indexing. The gateway looks up saved HTML by canonical path rather than using arbitrary query strings, cookies, authentication state, or POST bodies as public cache keys. That separation prevents a shared page snapshot from inadvertently standing in for user-specific output.
How publication handles edits, stale jobs, and failures
In the described system, the admin publication and invalidation of affected routes share a database transaction. A hotel edit can affect more than its own page: destination pages, bare search, the home page, offer pages, and articles that embed catalog records may also need new captures. A rename includes the old paths so their stored documents can be removed.
Rank #2
The worker leases due jobs with PostgreSQL FOR UPDATE SKIP LOCKED. Before saving, it checks the desired version under a row lock. If content changed while the browser was rendering, that output is discarded instead of replacing a newer version. Successful HTML is saved atomically with job completion; errors release the job for exponential-backoff retry, and partial HTML is not published.
A previous complete snapshot can remain available while a replacement is being generated. A new route with no saved document can return 503 with Retry-After: 5 until its first successful capture. That makes publication readiness a deployment concern: Chauhan recommends completing publication before sending production traffic or crawlers to a new path. A route that remains pending warrants checking worker logs and the job table.
Cache and security boundaries
The public cache policy in Chauhan’s implementation is max-age=60, stale-while-revalidate=300. Those are configuration values for this site, not general performance findings. A saved edit may therefore take time to appear in cached responses. When a route is unpublished or renamed, its stored document is removed, but already cached copies may persist for the declared cache period.
Rank #3
The internal source endpoint requires a sufficiently long token, accepts only recognized canonical routes, and returns private/no-store and noindex headers. Chauhan also says it should be blocked from public ingress; a robots directive is not a security boundary. The worker limits fetched resources to its configured API origin.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsCapture checks described in the implementation reject pages with missing canonical metadata, unrendered islands, unexpected executable scripts, browser errors, password or antiforgery inputs, or token strings. These are checks in one publishing system, not a guarantee that arbitrary application data is safe to cache. The core safeguard is an explicit allowlist of canonical public routes, with user-specific content kept on a separate live/private path.
How this differs from other rendering approaches
These distinctions reflect Chauhan’s conceptual comparison, not an independent survey of current framework capabilities.
| Approach | When HTML is generated | What it means for visitors and updates |
|---|---|---|
| SSR | Per request, potentially with caching | Can fit request-specific data naturally, but uncached rendering and data access add request-path work; caching needs careful boundaries. |
| SSG | At build time | Pages can be served simply; edits or new catalog pages may require a rebuild and redeploy unless another regeneration mechanism exists. |
| ISR | After revalidation | A stale copy may be available while a page regenerates; refresh rules and stale windows need management. |
| PPR | A static shell is prepared while dynamic regions render or stream at request time | That is not this design: its public snapshot is a complete shared document, with user-specific routes left live. |
| Snapshot worker | After edits or releases, in a background browser job | Razor output and browser-mounted islands are stored in PostgreSQL. Chromium is kept off visitor requests, at the cost of publication delay, possible first-capture 503s, and operating the database and browser worker. |
The worker’s distinguishing choice is to queue captures on edits or releases rather than wait for a visitor request to trigger regeneration. It trades request-time browser work for a publishing pipeline that must keep up with changes.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.What the design establishes about SEO—and what it does not
For a published hotel URL, the initial HTML response is intended to contain the heading, description, links, gallery markup, and metadata before a visitor’s browser executes React. Chauhan describes canonical URLs, page-specific titles and descriptions, Hotel JSON-LD, a sitemap of published canonical routes, and noindex,follow on filtered search URLs. This makes the main public content available in the initial response; it does not establish that a search engine will index or rank a page. A sitemap is a discovery hint, and structured data does not guarantee a rich result.
New routes also have a publication window: until their first capture succeeds, they may return 503. Canonical links are embedded at capture time, so changing PUBLIC_ORIGIN requires republishing. Old cached pages and assets also need consideration when changing the site origin or retiring content.
The article reports no measured Lighthouse score, controlled performance comparison, or named performance study. A Lighthouse run evaluates a loaded page and is not a direct test of what a no-JavaScript crawler receives. To assess the actual output, inspect the published HTML, canonical links, robots directives, sitemap, and metric breakdowns, and repeat audits under consistent conditions. Chunks, CSS, images, fonts, server and cache behavior, hydration, and audit settings can all affect results.
Development and deployment implications
The article distinguishes live Razor/Vite development from snapshot development. In the repository’s documented setup, pnpm dev runs live Razor pages with Vite HMR, while pnpm dev:snapshots runs the Vite manifest, .NET process, and continuous worker. These are repository-specific commands, so confirm them against the current repository before using them.
A production-like snapshot workflow needs PostgreSQL, an internal token, and Playwright Chromium. The worker’s asset fingerprint includes the Vite manifest, relevant source files, and PUBLIC_ORIGIN; a changed fingerprint requeues stored pages. Because saved HTML can refer to hashed assets, older assets must remain available while snapshots still reference them. In snapshot mode, the API should restart after rebuilding its manifest.
Chauhan’s summary is: “Razor owns the pages. React owns a few interactions. A separate browser publishes the finished HTML before anyone visits.” It captures the division of work in this particular implementation—not a universal rule for .NET and React sites.
Source
Piyush Chauhan, “How I Built a .NET + React Islands Site With a Playwright Snapshot Worker”, DEV Community. The retrieved page showed “Sep 26” without a year. The author’s linked repository is dotnet-islands; it has not been independently inspected or run for this article.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




