Client-side rendering

클라이언트 사이드 렌더링

The browser downloads and runs JavaScript before it builds the view from a minimal HTML shell.

···
html
<section class="stage"><div class="heading">CSR <span class="hint">empty shell → script → UI</span></div><div class="csr-grid"><div class="tile shell">HTML<div class="blank">empty</div></div><div class="arrow">→</div><div class="tile bundle">JS<div class="bars"><i></i><i></i><i></i></div></div><div class="arrow">→</div><div class="tile result">VIEW<div class="mock"><i></i><i></i></div></div></div><div class="caption">The view waits for the script to execute</div></section>
css
.stage{width:min(94vw,780px);height:min(88vh,322px);padding:clamp(12px,3vmin,20px);border:1px solid var(--line);border-radius:16px;background:var(--surface);box-shadow:0 12px 36px #0001;display:flex;flex-direction:column;gap:clamp(7px,2vmin,14px);font:500 clamp(12px,3.5vmin,17px)/1.3 var(--font-sans,system-ui,sans-serif);overflow:hidden}.heading{display:flex;justify-content:space-between;align-items:center;gap:8px;font-weight:750;color:var(--accent)}.hint{color:var(--muted);font-size:clamp(12px,3.2vmin,14px);font-weight:500}.caption{margin-top:auto;color:var(--muted);font-size:clamp(12px,3.2vmin,14px)}.row{display:flex;gap:clamp(5px,1.5vmin,12px);align-items:center}.tile{padding:clamp(6px,1.8vmin,13px);border:1px solid var(--line);border-radius:9px;background:var(--bg);text-align:center;min-width:0}.arrow{color:var(--accent);font-weight:800}.stage [data-on]{transition:background .3s,border-color .3s,opacity .3s,transform .3s}.stage .on{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 14%,var(--surface));color:var(--accent)}.csr-grid{display:flex;align-items:center;justify-content:center;gap:clamp(3px,1.3vmin,12px);flex:1}.csr-grid .tile{flex:1;max-width:150px;height:70%;display:flex;flex-direction:column;justify-content:space-between;font-weight:700}.blank{border:1px dashed var(--line);border-radius:6px;color:var(--muted);padding:10px 2px;font-weight:400}.bars,.mock{display:flex;flex-direction:column;gap:5px}.bars i,.mock i{height:8px;border-radius:5px;background:var(--line)}.bars i:nth-child(2),.mock i:nth-child(2){width:65%}.bundle,.result{opacity:.35;transition:opacity .3s}.stage[data-step="1"] .bundle,.stage[data-step="2"] .bundle,.stage[data-step="2"] .result{opacity:1}.stage[data-step="1"] .bars i,.stage[data-step="2"] .bars i{background:var(--accent-3)}.stage[data-step="2"] .mock i{background:var(--accent)}.stage small,.stage button,.stage .cards b,.stage .phase b,.stage .pair b,.stage .tree-mini b{font-size:clamp(12px,3.2vmin,14px)!important}
js
const stage=document.querySelector(".stage");let step=0;function tick(){stage.dataset.step=String(step);step=(step+1)%3}tick();let timer=setInterval(tick,950);stage.addEventListener("pointerdown",()=>{clearInterval(timer);tick()})

Client-side rendering (CSR) sends a small HTML shell, then JavaScript creates the content in the browser. It works well for ongoing state changes and in-app navigation.

The first view depends on downloading, parsing, and running the script. Large bundles or slow networks delay content, so splitting and loading states matter.

When to use

Consider it for interactive signed-in apps where ongoing interaction matters more than initial crawlable content.

Open as page ↗