Skip to main content

Overview

SAAC Frontend uses Vite’s Hot Module Replacement (HMR) with React Fast Refresh to provide instant feedback during development. Changes to your code appear in the browser without requiring a full page reload, preserving application state.

How HMR Works

1

File Change Detection

Vite watches your source files for changes using native ES modules
2

Module Update

When you save a file, Vite sends the update to the browser via WebSocket
3

Hot Update

The browser applies the update without refreshing the page
4

State Preservation

React Fast Refresh preserves component state during updates

React Fast Refresh

Configuration

React Fast Refresh is enabled via the Vite plugin:
vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [react(), tailwindcss()],
})
The @vitejs/plugin-react-swc uses SWC compiler which provides faster Fast Refresh compared to the Babel-based plugin.

What Gets Hot Reloaded

Component changes apply instantly with state preservation:
src/App.tsx
function App() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>
        count is {count}
      </button>
      <p>
        Edit <code>src/App.tsx</code> and save to test HMR
      </p>
    </div>
  )
}
Behavior:
  • Edit the JSX or styling → HMR updates, state preserved
  • Click button to set count to 5
  • Edit the component
  • Count remains 5 after HMR update

State Preservation

When State is Preserved

JSX Changes

Modifying component rendering logic preserves state

Style Updates

CSS and className changes preserve state

Event Handlers

Updating onClick and other handlers preserves state

Helper Functions

Non-hook function changes within components preserve state

When State is Reset

Adding, removing, or reordering hooks causes state reset:
// Before
function Component() {
  const [count, setCount] = useState(0)
  return <div>{count}</div>
}

// After - adding a new hook resets state
function Component() {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('') // ← New hook
  return <div>{count}</div>
}
Renaming a component function causes remount:
// Before
function MyComponent() {
  const [count, setCount] = useState(0)
  return <div>{count}</div>
}

// After - rename causes reset
function RenamedComponent() {
  const [count, setCount] = useState(0)
  return <div>{count}</div>
}
Changing default to named export or vice versa:
// Before
export default function App() { /* ... */ }

// After - export change causes reset
export function App() { /* ... */ }

Development Experience

Instant Feedback

src/App.tsx
function App() {
  const [count, setCount] = useState(0)

  return (
    <div className="card">
      <button onClick={() => setCount((count) => count + 1)}>
        count is {count}
      </button>
      <p>
        Edit <code>src/App.tsx</code> and save to test HMR
      </p>
    </div>
  )
}
Try it:
  1. Start dev server: npm run dev
  2. Click the button several times (e.g., count = 5)
  3. Edit the text “count is” to “clicks:”
  4. Save the file
  5. Notice: count remains 5, only text updates

Fast Build Times

Initial Startup

Vite starts in milliseconds using native ES modules

HMR Updates

Updates apply in less than 100ms regardless of app size

SWC Compilation

20x faster compilation with SWC instead of Babel

No Bundling

Development mode serves unbundled ES modules

Troubleshooting HMR

Common Issues

Symptoms: Changes require manual page refreshSolutions:
  1. Check if dev server is running: npm run dev
  2. Verify WebSocket connection in browser DevTools > Network > WS
  3. Check for syntax errors in the console
  4. Restart the dev server
# Restart dev server
# Press Ctrl+C to stop
npm run dev
Causes:
  • Syntax errors in the file
  • Changes to files outside src/
  • Modifications to vite.config.ts
  • Changes to index.html
Expected behavior:
  • vite.config.ts changes → requires server restart
  • index.html changes → requires full reload
  • Component changes → should use HMR
Common causes:
// ❌ Anonymous component - state resets
export default () => {
  const [count, setCount] = useState(0)
  return <div>{count}</div>
}

// ✅ Named component - state preserved
function App() {
  const [count, setCount] = useState(0)
  return <div>{count}</div>
}
export default App
Solution: Always use named function components
Checklist:
  • ✅ File saved?
  • ✅ No TypeScript errors?
  • ✅ Component exported correctly?
  • ✅ Browser tab is active?
Force refresh:
  • Press r in the Vite dev server terminal
  • Or manually refresh: Ctrl+Shift+R (hard reload)

Performance Tips

1

Keep Components Small

Smaller components update faster:
// ✅ Good - focused component
function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

// ⚠️ Less optimal - large component
function App() {
  // Hundreds of lines...
}
2

Use Code Splitting

Split large features for faster HMR:
import { lazy, Suspense } from 'react'

const Dashboard = lazy(() => import('./components/Dashboard'))

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Dashboard />
    </Suspense>
  )
}
3

Minimize Dependencies

Large dependencies can slow HMR. Import only what you need:
// ✅ Good - tree-shakeable
import { useState, useEffect } from 'react'

// ❌ Avoid - imports everything
import * as React from 'react'

HMR API (Advanced)

For custom HMR behavior, use Vite’s HMR API:
if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    // Custom HMR logic
  })
  
  import.meta.hot.dispose(() => {
    // Cleanup before update
  })
}
Most React applications don’t need custom HMR logic. The @vitejs/plugin-react-swc handles HMR automatically with Fast Refresh.

Development Workflow

1

Start dev server

npm run dev
Vite starts and shows the local URL (usually http://localhost:5173)
2

Make changes

Edit any .tsx, .ts, .css file in src/
3

Save and see updates

Changes appear instantly in the browser with state preserved
4

Check for errors

  • Terminal shows build errors
  • Browser console shows runtime errors
  • Vite overlay displays errors in the browser

Dev Server Commands

  • r - Restart server
  • u - Show server URLs
  • o - Open in browser
  • q - Quit server

Browser DevTools

  • Console: Runtime errors and logs
  • Network > WS: WebSocket connection
  • Elements: Inspect DOM changes
  • React DevTools: Component state

Build docs developers (and LLMs) love