Skip to content
← CSR
Edit CSR
Name
Also known as (comma separated)
Brief definition
Client-Side Rendering.
Notes
Tier
Core
Useful
Reference
Deleted
Controls schema (JSON array)
[ { "id": "bundleSize", "type": "number", "label": "JS bundle size (KB)", "default": 340, "min": 50, "max": 2000, "step": 10 }, { "id": "stage", "type": "select", "label": "Timeline stage", "default": "loading", "options": [ "blank", "loading", "rendered" ] }, { "id": "accent", "type": "color", "label": "Accent colour", "default": "#BFD9F2", "presets": [ "#BFD9F2", "#A8E6C1", "#F5C2E0", "#D9C8F0", "#E8A020", "#241430" ] }, { "id": "showTimes", "type": "toggle", "label": "Show elapsed time", "default": true } ]
Templates (JSON object keyed by format)
{ "html": "<div class=\"csr csr--{{stage}}\" role=\"img\" aria-label=\"CSR timeline, currently at {{stage}}\">\n <div class=\"csr__timeline\">\n <span class=\"csr__step csr__step--blank\">Blank HTML{{#if showTimes}}<small>0ms</small>{{/if}}</span>\n <span class=\"csr__step csr__step--loading\" style=\"--a:{{accent}};\">Download {{bundleSize}}KB JS{{#if showTimes}}<small>~600ms</small>{{/if}}</span>\n <span class=\"csr__step csr__step--rendered\" style=\"--a:{{accent}};\">Render + hydrate{{#if showTimes}}<small>~900ms</small>{{/if}}</span>\n </div>\n <div class=\"csr__screen\">\n <span class=\"csr__preview csr__preview--blank\"> </span>\n <span class=\"csr__preview csr__preview--loading\">◠</span>\n <span class=\"csr__preview csr__preview--rendered\" style=\"color:{{accent}};\">Page content</span>\n </div>\n</div>\n<style>\n.csr { font: 12px/1.5 system-ui, sans-serif; color: #241430; background: #FAF7F2; padding: 16px; border-radius: 12px; width: 320px; box-sizing: border-box; }\n.csr__timeline { display: flex; gap: 6px; margin-bottom: 14px; }\n.csr__step { flex: 1; text-align: center; padding: 8px 4px; border-radius: 8px; background: #24143010; font-size: 10px; font-weight: 600; opacity: .4; }\n.csr__step small { display: block; font-weight: 400; color: #24143099; margin-top: 3px; }\n.csr--blank .csr__step--blank, .csr--loading .csr__step--loading, .csr--rendered .csr__step--rendered { opacity: 1; background: var(--a, #24143010); }\n.csr__screen { height: 56px; display: grid; place-items: center; background: #ffffff; border: 1.5px dashed #24143020; border-radius: 8px; font-size: 20px; font-weight: 700; }\n.csr__preview { display: none; }\n.csr--blank .csr__preview--blank, .csr--loading .csr__preview--loading, .csr--rendered .csr__preview--rendered { display: block; }\n@media (prefers-reduced-motion: no-preference) { .csr__preview--loading { animation: csr-spin 1s linear infinite; } }\n@keyframes csr-spin { to { transform: rotate(360deg); } }\n</style>", "tailwind": "<div class=\"csrtw csrtw--{{stage}} font-sans text-[#241430] bg-[#FAF7F2] p-4 rounded-xl w-[320px] box-border\" role=\"img\" aria-label=\"CSR timeline, currently at {{stage}}\">\n <div class=\"flex gap-1.5 mb-3.5\">\n <span class=\"csrtw__step csrtw__step--blank flex-1 text-center py-2 px-1 rounded-lg bg-[#24143010] text-[10px] font-semibold opacity-40\">Blank HTML{{#if showTimes}}<small class=\"block font-normal text-[#24143099] mt-0.5\">0ms</small>{{/if}}</span>\n <span class=\"csrtw__step csrtw__step--loading flex-1 text-center py-2 px-1 rounded-lg bg-[#24143010] text-[10px] font-semibold opacity-40\" style=\"--a:{{accent}};\">Download {{bundleSize}}KB JS{{#if showTimes}}<small class=\"block font-normal text-[#24143099] mt-0.5\">~600ms</small>{{/if}}</span>\n <span class=\"csrtw__step csrtw__step--rendered flex-1 text-center py-2 px-1 rounded-lg bg-[#24143010] text-[10px] font-semibold opacity-40\" style=\"--a:{{accent}};\">Render + hydrate{{#if showTimes}}<small class=\"block font-normal text-[#24143099] mt-0.5\">~900ms</small>{{/if}}</span>\n </div>\n <div class=\"h-14 grid place-items-center bg-white border border-dashed border-[#24143020] rounded-lg text-xl font-bold\">\n <span class=\"csrtw__preview csrtw__preview--blank hidden\"> </span>\n <span class=\"csrtw__preview csrtw__preview--loading hidden\">◠</span>\n <span class=\"csrtw__preview csrtw__preview--rendered hidden\" style=\"color:{{accent}};\">Page content</span>\n </div>\n</div>\n<style>\n.csrtw--blank .csrtw__step--blank, .csrtw--loading .csrtw__step--loading, .csrtw--rendered .csrtw__step--rendered { opacity: 1; background: var(--a, #24143010); }\n.csrtw--blank .csrtw__preview--blank, .csrtw--loading .csrtw__preview--loading, .csrtw--rendered .csrtw__preview--rendered { display: block; }\n</style>", "react": "export function Csr({\n bundleSize = {{bundleSize}},\n stage = \"{{stage}}\",\n accent = \"{{accent}}\",\n showTimes = {{showTimes}},\n}) {\n const steps = [\n { id: \"blank\", label: \"Blank HTML\", time: \"0ms\" },\n { id: \"loading\", label: `Download ${bundleSize}KB JS`, time: \"~600ms\" },\n { id: \"rendered\", label: \"Render + hydrate\", time: \"~900ms\" },\n ];\n const previews = { blank: \"\\u00A0\", loading: \"\\u25F0\", rendered: \"Page content\" };\n return (\n <div role=\"img\" aria-label={`CSR timeline, currently at ${stage}`} style={{ font: \"12px/1.5 system-ui, sans-serif\", color: \"#241430\", background: \"#FAF7F2\", padding: 16, borderRadius: 12, width: 320, boxSizing: \"border-box\" }}>\n <div style={{ display: \"flex\", gap: 6, marginBottom: 14 }}>\n {steps.map((s) => (\n <span key={s.id} style={{ flex: 1, textAlign: \"center\", padding: \"8px 4px\", borderRadius: 8, fontSize: 10, fontWeight: 600, opacity: s.id === stage ? 1 : .4, background: s.id === stage ? accent : \"#24143010\" }}>\n {s.label}\n {showTimes && <small style={{ display: \"block\", fontWeight: 400, color: \"#24143099\", marginTop: 3 }}>{s.time}</small>}\n </span>\n ))}\n </div>\n <div style={{ height: 56, display: \"grid\", placeItems: \"center\", background: \"#ffffff\", border: \"1.5px dashed #24143020\", borderRadius: 8, fontSize: 20, fontWeight: 700, color: stage === \"rendered\" ? accent : \"#241430\" }}>{previews[stage]}</div>\n </div>\n );\n}" }
Save
History