hotdogjs

๐ŸŒญ Hotdog JS

What goes well with Bun? Hotdogs! ๐ŸŒญ๐ŸŒญ๐ŸŒญ

Hotdogjs is a LiveView web framework optimized to run on Bun.

Hotdogjsโ€™s key features are:

Github repo: https://github.com/floodfx/hotdogjs

๐ŸŽ Feedback is a gift! ๐ŸŽ Please ask questions, report issues, give feedback, etc by opening an issue. I love hearing from you!

How does Hotdogjs work?

At a high level, LiveViews automatically detect registered events (clicks, form updates, etc.) and routes these events (and metadata) from client to server over a websocket. When the server receives an event, it routes it to a developer defined handler which can update the server state and kicks off a re-render. The server then calculates a view diff and sends this diff back to the client which is automatically applied to the DOM.

All of the complicated stuff (communication protocol, websocket lifecycle, state management, diff calculation and application, etc.) is handled automatically by Hotdogjs. The developer only needs to focus on the business logic of their application using the simple, yet powerful programming paradigm of LiveViews.

What type of projects is Hotdogjs best for?

What type of projects is Hotdogjs NOT best for?

Quick Start

bun create hotdogjs
# (follow the prompts)
cd my-hotdogjs-app
bun install
bun dev

Open http://localhost:3000 in your browser.

Recent Talk at DenverScript Feb 25, 2025

HotdogJS - A Bun-optimized LiveView Framework

Link to Youtube

Thanks to DenverScript for the invite and great community!

Run the Examples

Anatomy of a Hotdogjs project

View Routing

A View is defined by creating a file in the views/ directory. We use Bunโ€™s File System Router to route requests to the appropriate View and extract path and query parameters that are passed to the View as params in the mount method. You can override the directory that the Views are located in by setting the viewsDir option in the hotdogjs-conf.toml file.

Basics of a (Live) View

A View is a web page that responds to events, updates its state, and generates HTML diffs. Views are initially rendered as HTML over HTTP. Once rendered, the View automatically connects to the server via a websocket. When connected, the View automatically sends events from the client and receives then automatically applies diffs to the DOM. You should extend BaseView to create your own View.

View API

View have the following API:

Counter View Example

There are many more examples in the packages/examples directory but just to get a feel for LiveViews, here is a simple counter example:

export default class Increment extends BaseView<AnyEvent> {
  count: number = 0;

  handleEvent(ctx: ViewContext, event: AnyEvent) {
    if (event.type === "inc") this.count++;
  }

  render() {
    return html`
      <h3>${this.count}</h3>
      <button hd-click="inc">+</button>
    `;
  }
}

Event Bindings / HTML Attributes

There are four main types of user events that user can trigger:

You can add the following attributes to your HTML elements to send events (and custom data) to the server based on user interactions:

Binding Attribute Supported
Custom Data hd-value-* โœ…
Click Events hd-click โœ…
Click Events hd-click-away โœ…
Form Events hd-change โœ…
Form Events hd-submit โœ…
Form Events hd-feedback-for โœ…
Form Events hd-disable-with โœ…
Form Events hd-trigger-action โŒ
Form Events hd-auto-recover โŒ
Focus Events hd-blur โœ…
Focus Events hd-focus โœ…
Focus Events hd-window-blur โœ…
Focus Events hd-window-focus โœ…
Key Events hd-keydown โœ…
Key Events hd-keyup โœ…
Key Events hd-window-keydown โœ…
Key Events hd-window-keyup โœ…
Key Events hd-key โœ…
DOM Patching hd-update โœ…
DOM Patching hd-remove โŒ
Client JS hd-hook โœ…
Rate Limiting hd-debounce โœ…
Rate Limiting hd-throttle โœ…
Static Tracking hd-track-static โŒ

Components

Components are small, reusable pieces of UI that can be used across multiple Views. You should put components somewhere outside of the views/ directory so they arenโ€™t routed to accidentally. Components can be stateless or stateful and encapsulate their own state in the latter case. Components are rendered using the component method which takes a Component class and returns an instance of that class.

Component API

Components have the following API:

JS Commands

User events (clicks, etc) typically trigger a server-side event which updates the state and re-renders the HTML. Sometimes you want to update the DOM without (or in addition to) initiating a server round trip. This is where JS Commands come in.

JS Commands support a number of client-side DOM manipulation function that can be used to update the DOM without a server round trip. These functions are:

JS Command Syntax

JS Commands are used in the render function as part of the HTML markup:

render() {
  return <div hd-click="${new JS().push("increment")}">Increment</div>
}

โ€œChainableโ€ (i.e., fluent) Syntax

JS Commands are โ€œchainableโ€ (i.e., fluent) so you can chain multiple commands together as needed and they will be executed in the order they are called:

render() {
  return <div hd-click="${new JS().hide().push("increment")}">Increment and hide</div>
}

Ejecting a Hotdogjs project

When using the bun create hotdogjs command, the generated project comes with a eject script that, when run, will generate the basic server configuration, client-side javascript file, hotdogjs-conf.toml, and update the package.json. This gives you the ability to customize the project to your liking including full control over the http server and client-side javascript file.

How is Hotdogjs different from other frameworks?

Hotdogjs supports both server rendering and client interactivity in a single programming model called LiveView. Hotdogjs is built on the following principles:

Configuration / hotdogjs-conf.toml

If you want/need to override the default configuration of a Hotdogjs project, you can do so by creating a hotdogjs-conf.toml file in the root of your project.

The following configuration options are supported:

Alternatively, you can override the default configuration by setting the following environment variables:

Roadmap / Known Issues

Bun features we use

Bun is fast as heck and provides some powerful features out-of-the-box that Hotdogjs takes advantage of. We specifically lean into Bun features on purpose since this project seeks to take advantage of Bun in its entirety. Below is a list of Bun features that are used in Hotdogjs:

Bun features we donโ€™t use yet

How is this different from LiveViewJS?

I wrote LiveViewJS and got annoying to abstract it in such a way to support Node and Deno. Bun was intriguing because if its speed and native Typescript support. Hotdogjs started as a way to learn more about Bun and re-implement LiveViews for the Bun runtime and based on other LiveView implementations Iโ€™d learned from along the way. Overall, the implementation and APIs are similar with lots of simplifications in Hotdogjs to make it more ergonomic and more powerful with almost zero dependencies.

I also wrote or helped write the following LiveView frameworks: