Next.js CSR Preset
Preset file: presets/nextjs-csr.toml
Works with Next.js pages that rely on client-side data fetching without getServerSideProps or getStaticProps.
When PRISM is Needed
Next.js supports multiple rendering strategies. PRISM is most useful for pages that fetch data client-side:
| Rendering Strategy | PRISM Needed? |
|---|---|
getServerSideProps (SSR) | No -- HTML is already rendered server-side |
getStaticProps + getStaticPaths (SSG) | No -- HTML is pre-built at build time |
getStaticProps with revalidate (ISR) | No -- HTML is pre-built and revalidated |
Client-side fetching (useEffect, useSWR, React Query) | Yes -- page shell is empty until JS runs |
| Hybrid (SSR shell + client-side enrichment) | Maybe -- depends on what content is client-rendered |
Static export (next export) | Yes -- no server, all rendering is client-side |
Key Configuration
[render]
wait_for = "networkidle"
timeout_secs = 15
[render.postprocess]
enabled = true
strip_scripts = true
strip_noscript = true
strip_comments = true
strip_event_handlers = true
strip_hydration_attrs = true # Removes data-reactroot
resolve_lazy_images = true
[render.content_validation]
enabled = true
min_text_length = 100
require_title = true
min_html_bytes = 1024
NEXT_DATA Handling
Next.js embeds a <script id="__NEXT_DATA__"> tag containing the page props as JSON. When strip_scripts = true, this script is removed along with all other scripts.
This is correct for bot-only mode -- bots do not need the hydration data. The rendered HTML already contains all the content.
If you need to preserve __NEXT_DATA__ (for example, in render-all mode where the page needs to hydrate), disable script stripping:
[render.postprocess]
strip_scripts = false
Hydration Attributes
Next.js uses data-reactroot on the root element (inherited from React). With strip_hydration_attrs = true, this is removed from the rendered output.
Route Exclusions
The preset excludes Next.js build artifacts and API routes:
[routes]
exclude = [
"/api/**",
"/_next/**",
"**/*.js", "**/*.css", "**/*.json", "**/*.xml",
"**/*.png", "**/*.jpg", "**/*.gif", "**/*.svg", "**/*.ico",
"**/*.woff", "**/*.woff2", "**/*.ttf", "**/*.wasm", "**/*.map",
"/manifest.json", "/sw.js", "/robots.txt", "/sitemap.xml",
]
The /_next/** exclusion is important -- it covers all Next.js static assets, chunks, and webpack hot module replacement endpoints.
SPA Status Codes
For Next.js apps with client-side 404 handling, use the status meta tag:
import Head from 'next/head';
function Custom404() {
return (
<>
<Head>
<title>Page Not Found</title>
<meta name="render:status_code" content="404" />
</Head>
<h1>404 - Page Not Found</h1>
</>
);
}
Enable in your PRISM config:
[render]
status_from_meta = true
Partial Rendering Use Case
For hybrid Next.js apps where some routes use SSR and others use client-side fetching, you can scope PRISM to only the CSR routes:
[routes]
include = [
"/dashboard/**",
"/search/**",
"/user/**",
]
exclude = [
"/api/**",
"/_next/**",
# ... asset patterns
]
This way, SSR routes are proxied directly (they already have rendered HTML) while CSR routes go through PRISM's rendering pipeline.
Full Preset
[render]
wait_for = "networkidle"
timeout_secs = 15
[render.postprocess]
enabled = true
strip_scripts = true
strip_noscript = true
strip_comments = true
strip_event_handlers = true
strip_hydration_attrs = true
resolve_lazy_images = true
[render.content_validation]
enabled = true
min_text_length = 100
require_title = true
min_html_bytes = 1024
[routes]
include = ["/**"]
exclude = [
"/api/**",
"/_next/**",
"**/*.js", "**/*.css", "**/*.json", "**/*.xml",
"**/*.png", "**/*.jpg", "**/*.gif", "**/*.svg", "**/*.ico",
"**/*.woff", "**/*.woff2", "**/*.ttf", "**/*.wasm", "**/*.map",
"/manifest.json", "/sw.js", "/robots.txt", "/sitemap.xml",
]