bun_bun_bundle

Zero-dependency asset bundler with CSS hot-reloading, fingerprinting, live reload, and a flexible plugin system. Works with any Crystal app.

Bun, Bun, Bundle for Crystal

CI Version

A self-contained asset bundler for Crystal powered by Bun. No development dependencies, no complex configuration. Lightning fast builds with CSS hot-reloading, fingerprinting, live reload, and a flexible plugin system. Works with Kemal or any Crystal HTTP server.

This is the Crystal version of the Ruby gem with the same name.

[!NOTE] If you are using the Lucky framework, this functionality is already built in. You do not need this shard.

Installation

  1. Add the dependency to your shard.yml:

    dependencies:
      bun_bun_bundle:
        codeberg: fluck/bun_bun_bundle
    
  2. Run shards install

  3. Make sure Bun is installed:

    curl -fsSL https://bun.sh/install | bash
    
  4. Install the BunBunBundle CLI (Ruby gem):

    gem install bun_bun_bundle
    

Configuration

BunBunBundle reads its configuration from config/bun.json. This file is shared between the JavaScript bundler and this shard.

{
  "entryPoints": {
    "js": ["src/js/app.js"],
    "css": ["src/css/app.css"]
  },
  "outDir": "public/assets",
  "publicPath": "/assets",
  "manifestPath": "public/bun-manifest.json",
  "devServer": {
    "host": "127.0.0.1",
    "port": 3002
  }
}

All fields have sensible defaults. See the BunBunBundle documentation for the full configuration reference.

Usage with Kemal

Setup

Load the asset manifest and register the development cache handler:

require "kemal"
require "bun_bun_bundle"

BunBunBundle::Manifest.load

# In development, prevent stale asset caching:
add_handler BunBunBundle::DevAssetCacheHandler.new

Helpers in views

Include BunBunBundle::Helpers and BunBunBundle::ReloadTag in your layout or view:

require "kemal"
require "bun_bun_bundle"

BunBunBundle::Manifest.load

class Layout
  include BunBunBundle::Helpers
  include BunBunBundle::ReloadTag

  def initialize(@content : String)
  end

  ECR.def_to_s "src/views/layout.ecr"
end

get "/" do
  content = "<h1>Hello</h1>"
  Layout.new(content).to_s
end

add_handler BunBunBundle::DevAssetCacheHandler.new

Kemal.run

In your layout template (src/views/layout.ecr):

<!DOCTYPE html>
<html>
<head>
  <%= bun_css_tag("css/app.css") %>
  <%= bun_js_tag("js/app.js") %>
  <%= bun_reload_tag %>
</head>
<body>
  <%= @content %>
  <%= bun_img_tag("images/logo.png", alt: "My App") %>
</body>
</html>

Running

Start the BunBunBundle dev server and your Kemal app:

# Terminal 1: start the asset bundler with file watcher
bun_bun_bundle dev

# Terminal 2: start your app
crystal run src/app.cr

For production, build fingerprinted assets first:

bun_bun_bundle build --prod
crystal build --release src/app.cr

API reference

BunBunBundle::Manifest

  • BunBunBundle::Manifest.load - loads the asset manifest at compile time. Call this once during app setup.
  • BunBunBundle::Manifest.asset("js/app.js") - returns the fingerprinted path. Checked at compile time with typo suggestions.
  • BunBunBundle::Manifest.dynamic_asset("images/icon-#{name}.png") - returns the fingerprinted path at runtime. Use when you need string interpolation.
  • BunBunBundle::Manifest.css_entry_points - returns all CSS entry point keys. Used internally by the reload tag.

BunBunBundle::Helpers

Include this module to get:

  • bun_asset(path) - returns the public path for a manifest asset.
  • bun_js_tag(source, **options) - generates a <script> tag.
  • bun_css_tag(source, **options) - generates a <link> tag with automatic cache busting in development.
  • bun_img_tag(source, alt: nil, **options) - generates an <img> tag.

BunBunBundle::ReloadTag

Include this module to get:

  • bun_reload_tag - returns a <script> tag that connects to the dev server's WebSocket for CSS hot-reloading and live reload. Returns an empty string unless running in development (checks KEMAL_ENV, then CRYSTAL_ENV, defaults to "development").

BunBunBundle::DevAssetCacheHandler

An HTTP::Handler that sets no-cache headers on asset requests in development.

# Kemal:
add_handler BunBunBundle::DevAssetCacheHandler.new

# Plain HTTP::Server:
server = HTTP::Server.new([
  BunBunBundle::DevAssetCacheHandler.new,
  # ...other handlers...
])

BunBunBundle::Config

Reads config/bun.json and provides typed access to all configuration values. Shared between the JavaScript bundler and this shard.

config = BunBunBundle::Config.instance
config.public_path  # => "/assets"
config.out_dir      # => "public/assets"
config.dev_server.ws_url  # => "ws://127.0.0.1:3002"

License

MIT

Repository

bun_bun_bundle

Owner
Statistic
  • 1
  • 0
  • 0
  • 0
  • 1
  • 15 days ago
  • April 9, 2026
License

Links
Synced at

Wed, 29 Apr 2026 22:30:08 GMT

Languages