Modern SPAs (React & Vue) behave very differently from traditional websites when it comes to loading and rendering UI in the browser.
To understand performance, SEO, and UX, you must understand the Critical Rendering Path (CRP).
This post breaks down:
- What happens from the moment the browser requests your page
- What blocks rendering & why
- How React and Vue CSR differ
- How CSS strategies impact rendering
- How SSG/SSR change the flow
- What exactly is inside the main JavaScript bundle
Letβs go step by step.
π§ 1. What is the Critical Rendering Path?
The Critical Rendering Path (CRP) is:
β‘οΈ The set of steps the browser performs to convert HTML/CSS/JS into pixels on the screen.
It contains 5 main stages:
1. HTML Download & Parsing β DOM creation
2. CSS Download & Parsing β CSSOM creation (render-blocking)
3. JavaScript Download & Execution (often render-blocking)
4. Render Tree Construction (DOM + CSSOM)
5. Layout β Paint β Composite
Letβs break these down in detail π
π§΅ 2. Detailed CRP Steps & What Blocks Rendering
Step 1 β Load HTML & Build the DOM
What happens:
- Browser downloads the HTML file.
- Parses tags sequentially (top β bottom).
- Builds the DOM tree incrementally.
Render blocking?
β HTML itself is not render-blocking.
BUT:
β External <script> tags block HTML parsing unless they use defer or type="module".
β External <link rel="stylesheet"> blocks rendering until CSS finishes downloading.
Step 2 β Load & Parse CSS β Build CSSOM
What happens:
- Browser downloads all CSS files.
- Parses them β builds CSSOM.
Render blocking?
β CSS is always render-blocking
β Browser will not paint anything until CSSOM is ready
β This is why CSS-in-JS slows down first paint
Because:
Browser needs CSS rules to compute layout safely.
Step 3 β Load & Execute JavaScript
What happens:
- Browser downloads JS bundles.
- Parses + executes them on the main thread.
Render blocking?
πΈ Inline & external JS (without defer or module) BLOCK HTML parsing.
πΈ Even deferred JS is execution-blocking for CSR frameworks.
Why?
React/Vue need to execute the main JS bundle to generate the UI.
Step 4 β Build Render Tree (DOM + CSSOM)
Browser merges:
- DOM
- CSSOM
Render tree is built β only visible nodes with computed styles.
Render blocking?
β Requires DOM & CSSOM (so CSS blocks rendering).
β Does NOT need JS unless JS mutates DOM (CSR apps do this).
Step 5 β Layout β Paint β Composite
Browser computes:
- Size
- Position
- Colors
- Layers
Then paints pixels on screen.
Render blocking?
β Happens only after DOM + CSSOM + JS ready
β Extra style recalculations or forced reflows can delay painting
ποΈ 3. The Main JavaScript Bundle β Whatβs Actually Inside?
This is the biggest performance bottleneck in CSR apps.
Your main JS bundle contains:
1. The framework runtime
- React / Vue core libraries
- Reconciliation logic
- Virtual DOM implementation
2. All your components
Each component is:
- JS functions (React)
- Compiled templates β render functions (Vue)
3. Router & route components
SPA routing logic is inside the bundle.
4. Application bootstrap code
For React:
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
For Vue:
createApp(App).mount("#app");
5. State management, utilities, and 3rd-party libraries
- redux, zustand, jotai
- axios
- lodash
- charts, maps, etc.
6. CSS-in-JS runtime (if used)
styled-components / emotion:
- Parse JS styles
- Generate CSS
- Inject
<style>tags dynamically - This blocks render tree creation
7. Code needed for hydration (only SSG/SSR)
React/Vue inject interactivity into existing HTML.
βοΈ 4. Critical Render Path in React CSR
π Initial HTML
React CSR apps ship almost empty HTML:
<body>
<div id="root"></div>
<script src="/assets/main.js"></script>
</body>
No UI. Just root.
π React CSR CRP Workflow
Step 1: HTML β Empty DOM
Browser sees only a <div id="root">.
Step 2: CSS Blocks Render
Global CSS / Tailwind downloaded and parsed.
Step 3: JS Blocks Interactivity
Browser runs the main bundle:
- Initializes React runtime
- Builds virtual DOM
- Runs components
- Reconciles to real DOM
- Injects DOM into
#root
π― Only now the UI appears.
Render-blocking in React CSR:
| What | Blocks? | Why |
|---|---|---|
| CSS files | β Yes | Needed to compute styles |
| JS main bundle | β Yes | UI depends on React runtime |
| CSS-in-JS | β Extra blocking | Needs JS to generate CSS |
| Tailwind | β | Large CSS can slow CSSOM |
π§© CSS Scenarios in React CSR
Global CSS
β‘οΈ Render-blocking but fast
β‘οΈ CSSOM ready early
CSS Modules
β‘οΈ Compiled into global CSS
β‘οΈ Same CRP as global CSS
Tailwind CSS
β‘οΈ Large CSS file
β‘οΈ Slower CSSOM build
CSS-in-JS
β‘οΈ Slowest
β‘οΈ JS must run to create styles
β‘οΈ Inject <style> tags after render
β‘οΈ Causes layout thrashing
πΏ 5. Critical Render Path in Vue CSR
Vue CSR is similar but with key differences:
- Templates are pre-compiled into render functions
- Scoped CSS introduces attribute selectors (slightly slower)
π Initial HTML
<body>
<div id="app"></div>
<script src="/main.js"></script>
</body>
π Vue CSR CRP Workflow
Step 1 β HTML β DOM with empty root
Step 2 β Global CSS renders
Step 3 β JS executes
- Vue runtime starts
- Templates (compiled to render functions) execute
- Virtual DOM created
- Vue mounts to
#app - Scoped styles applied
Vue has smaller runtime & faster template execution.
π§© CSS Scenarios in Vue CSR
Global CSS
β‘οΈ Fastest
Scoped CSS
β‘οΈ Uses attributes like [data-v-123abc]
β‘οΈ Slightly slower CSSOM
CSS Modules in Vue
β‘οΈ Similar to JS frameworks
β‘οΈ Compiled into global CSS
CSS-in-JS
β‘οΈ Rare in Vue but slow like React
Tailwind
β‘οΈ Same implications as React
π₯ 6. CSR vs SSG vs SSR β CRP Comparison
| Mode | Initial HTML | FCP | JS Need | Hydration | Best For |
|---|---|---|---|---|---|
| CSR | Empty shell | Slowest | Mandatory | β No | Internal apps |
| SSG | Pre-rendered | Fast | Needed for interactivity | β Yes | Blogs/docs/marketing |
| SSR | Server-rendered | Fast | Needed for interactivity | β Yes | Dynamic pages (dashboards) |
π§± 7. What Changes in SSG?
SSG = HTML generated at build time.
SSG CRP:
- Browser downloads full HTML (content visible immediately)
- CSSOM parsed
- Browser paints early = fast FCP
- JS loads later β hydration
- App becomes interactive
π― Content visible early
β Interaction delayed
π§± 8. What Changes in SSR?
SSR = HTML generated on server per request.
SSR CRP:
- Browser receives pre-rendered HTML
- DOM built immediately
- CSS render-blocking
- Early paint
- JS hydrates to make it interactive
π― Best FCP
β οΈ Hydration is heavy β may hurt TTI
π 9. Commonly Overlooked CRP Realities in SPA Apps
1. JavaScript is the biggest performance bottleneck
CSR requires JS to create UI β slow on low-end devices.
2. Hydration is expensive in SSR/SSG
More components = slower hydration = poor TTI.
3. CSS-in-JS is always slower
- Execution cost
- Style injection
- Extra layout & paint cycles
4. Tailwind improves maintainability but affects CSSOM size
Large CSS file blocking rendering.
5. Rendering depends on device capability
JS execution time is 10Γ slower on low-end Android devices.
π Final Summary
React CSR
- Empty HTML
- JS must run to render UI
- CSS strategies heavily affect CRP
Vue CSR
- Similar to React but template compilation is faster
- Scoped CSS adds minor cost
SSG
- Best FCP
- Requires hydration
SSR
- Early paint
- Heavy hydration
Understanding CRP helps you optimize what truly matters:
- Reduce JS bundle size
- Avoid CSS-in-JS unless necessary
- Prefer SSR/SSG for public-facing pages
- Minimize CSS blocking
Top comments (0)