HUIJZER.XYZ

Live reloading for any generated website

2025-01-27

When generating a website (typically HTML and CSS files), it is often useful to have a live reload feature. This means that your browser will automatically reload the page when you make changes to the files via code. For example, say you write some code that generates a plot on a webpage, or that generates some WebAssembly module that is embedded in the page. In the past, I would use tools like webpack or try to manually establish a socket on the server and inject JavaScript in the page.

I recently found a much simpler solution. Just use Bash together with any server that can serve static files and injects live-reloading like live-server.

For example, with Rust say you generate a simple HTTP file from main.rs like this:

fn main() {
    let html = r#"
    <!DOCTYPE html>
    <html>
    <body>
        <h1>Hello, world!</h1>
    </body>
    </html>
    "#;

    std::fs::create_dir_all("_public").unwrap();

    let path = std::path::Path::new("_public/index.html");
    std::fs::write(path, html).unwrap();
}

This will create a _public/index.html file when you run cargo run.

Now you can add a little Bash script to serve the files. Let's call the file serve.sh:

#!/usr/bin/env bash

set -e

live-server --port 8080 _public/ & SERVER_PID=$!

# Trap to kill the server when the process exits. Without this, the port
# will remain in use and a new server will fail to start.
trap "kill $SERVER_PID" EXIT

cargo watch -x run

Here, & runs the server in the background. Whenever the script exits, the server process will be killed too allowing you to start a new server.

Give the script execute permissions:

chmod +x serve.sh

and run ./serve.sh:

$ ./serve.sh
[2025-01-27T18:25:19Z INFO  live_server::http_layer::listener] Listening on http://192.168.1.69:8080/
[2025-01-27T18:25:19Z INFO  live_server::file_layer::watcher] Listening on /Users/rik/git/tmp/_public
[Running 'cargo run -- generate']
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/tmp generate`
[Finished running. Exit status: 0]
[2025-01-27T18:25:19Z ERROR live_server::file_layer::watcher] SendError(())
[Running 'cargo run -- generate']
   Compiling tmp v0.1.0 (/Users/rik/git/tmp)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.16s
     Running `target/debug/tmp generate`
[Finished running. Exit status: 0]

If on the first run you get the following error:

[2025-01-27T18:26:48Z ERROR live_server::file_layer::watcher] SendError(())

then this can just be ignored. Everything should work fine.

Now you can make changes to the Rust code and the browser will automatically update the page.