Hot Reload
Developer experience is paramount in the Harpia ecosystem. For applications configured in Fullstack mode, the framework provides a native Hot Reload system for the client-side (browser), completely eliminating the friction of manually refreshing the page after every code change.
Important: The browser Hot Reload feature described in this guide is used exclusively when the application is in Fullstack mode. If you are building an API, server-level reloading is handled by Bun’s
--hotflag instead.
Although the browser reloads to display interface changes, it is important to remember that the background server process also updates code without restarting, thanks to Bun’s native engine.
How it Works
When you scaffold a Fullstack project using the create-harpia-app command (with the --fullstack flag), your project comes pre-configured with the complete Hot Reload infrastructure.
The architecture of Harpia’s Hot Reload works as follows:
- The Harpia server exposes an internal WebSocket route at
ws://localhost:{{ serverPort() }}/harpia/hr. - When you edit a client-side template, CSS, or JavaScript file, the server detects this change.
- A WebSocket message containing a
{ type: "reload" }payload is broadcasted to all active clients. - The browser intercepts the message and triggers a
location.reload(), updating the view instantly in under 100 milliseconds.
The Base Component
If you navigate to the components directory generated by the CLI, you will find the file responsible for injecting this logic into the front-end: hot-reload-component.txt (or .html depending on your engine).
The component’s logic is designed to be lightweight and resilient to connection drops, automatically attempting to reconnect if the server restarts:
@if checkEnv("ENV", "development")
<script nonce="{{ nonce }}">
(() => {
const WS_URL = "ws://localhost:{{ serverPort() }}/harpia/hr";
const RECONNECT_DELAY = 1000;
let ws;
let reloadTimeout = null;
function connect() {
ws = new WebSocket(WS_URL);
ws.addEventListener("message", (event) => {
try {
const msg = JSON.parse(event.data);
if (msg.type === "reload") {
if (reloadTimeout) clearTimeout(reloadTimeout);
reloadTimeout = setTimeout(() => {
location.reload();
reloadTimeout = null;
}, 100);
}
} catch (err) {
console.error("Error processing WebSocket message:", err);
}
});
ws.addEventListener("close", () => {
setTimeout(connect, RECONNECT_DELAY);
});
ws.addEventListener("error", () => {
ws.close();
});
}
connect();
})();
</script>
@endif
Notice the use of the @if checkEnv("ENV", "development") block. This ensures that the Hot Reload script is never served to your end users in a production environment. The code only exists during local development.
Base Layout Configuration
For automatic reloads to work globally across all routes of your web application, the Hot Reload component is injected directly into your main layout (typically layout-default.txt or default.html).
The Harpia generator script inserts the component at the end of the page body (<body> tag), injecting a server-generated nonce security key to ensure compliance with CSP (Content Security Policy):
@set nonce = generateNonce() @endset
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ... head tags ... -->
<title>{{ title }}</title>
</head>
<body>
@yield("body")
<!-- The Hot Reload component is injected here -->
@component("hot-reload", { nonce: "{{ nonce }}" })
<script type="module" src="/js/bundle.js"></script>
</body>
</html>
Configuration
The HotReload instance is configured via an options object in app/config/hot-reload.ts. The following properties are available:
// app/config/hot-reload.ts
import { HotReloadManager, type HotReloadInterface } from "@harpiats/common";
export const HotReloadOptions: HotReloadInterface = {
verbose: false,
watch: ["./modules", "./public", "./resources"],
extensions: [".css", ".html", ".js", ".ts"],
};
export const HotReload = new HotReloadManager(HotReloadOptions);
| Property | Type | Default | Description |
|---|---|---|---|
verbose | boolean | false | When true, logs every detected file change to the console. |
watch | string[] | — | List of directories to watch for file changes. |
extensions | string[] | — | File extensions that trigger a reload when modified. |
Disabling Hot Reload
If you need to completely disable Hot Reload (both on the frontend and the modular backend reload) and test the application exactly as it would run in production, simply change your ENV environment variable to production or run the server natively:
bun start/server.ts