with one click
hanami-assets
// Expert guidance on building, configuring, and managing Hanami Assets
// Expert guidance on building, configuring, and managing Hanami Assets
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | hanami-assets |
| description | Expert guidance on building, configuring, and managing Hanami Assets |
This skill provides expert guidance on building, configuring, and managing Hanami Assets (v2.x). It covers the asset structure, entry points, bundles, compilation, CLI commands, helpers, CDN configuration, and customization.
Understand the default asset layout
A new Hanami app provides:
.
├── app
│ ├── assets
│ │ ├── css
│ │ │ └── app.css
│ │ ├── images
│ │ │ └── favicon.ico
│ │ └── js
│ │ └── app.js
├── config
│ └── assets.js
├── package.json
└── public
└── assets
├── assets.json
├── app-HYVEQYF6.css
├── app-6PW7FGD5.js
└── favicon-5VHYTKP2.ico
app/assets/ — source assets directoryapp/assets/css/ — stylesheets (special directory)app/assets/js/ — JavaScript files (special directory)app/assets/images/ — static assets (images, fonts, etc.)config/assets.js — esbuild compilation configurationpublic/assets/ — compiled output directorypublic/assets/assets.json — manifest file for locating compiled assetsUnderstand slice asset layout
Slices have their own independent assets under their own assets/ directory:
slices/admin
└── assets
├── css
│ └── app.css
└── js
└── app.js
Compiled output goes to public/assets/_admin/ (underscored to prevent collisions).
Use the default entry point
The default entry point is app/assets/js/app.js:
import "../css/app.css";
Only files referenced by an entry point will be included in the final bundle.
Create multiple entry points
Create any directory under js/ with a file named app.js inside it:
app/assets/js/
├── app.js # Default entry point
└── signin/
├── app.js # Sign-in page entry point
└── resetPassword.js
// app/assets/js/signin/app.js
import "../../css/signin/app.css";
import { resetPassword } from "./resetPassword";
Supported extensions: .js, .mjs, .ts, .mts, .tsx, .jsx.
Understand entry point output
Each entry point generates its own bundle under public/assets/:
public/assets/
├── assets.json
├── app-GVDAEYEC.css
├── app-LSLFPUMX.js
└── signin/
├── app-JPZQ4M77.css
└── app-LSLFPUMX.js
Understand asset bundling
Compile assets for production
bundle exec hanami assets compile
Production output:
app-LSLFPUMX.js)public/assets/assets.jsonWatch assets in development
bundle exec hanami assets watch
Development output:
The hanami dev command starts hanami assets watch by default.
Understand environment differences
| Aspect | Production | Development |
|---|---|---|
| Filenames | Content-hashed | Matches source names |
| Minification | Yes | No |
| Source maps | Yes | No |
| Static serving | Disabled by default | Enabled |
HANAMI_SERVE_ASSETS | Set true to serve from app | Not needed |
Get asset URL via helpers
<%= asset_url("app.js") %>
# => "/assets/app-LSLFPUMX.js"
<%= asset_url("images/logo.png") %>
# => "/assets/images/logo.png"
Generate HTML tags for assets
<%= javascript_tag("app") %>
# => <script src="/assets/app-LSLFPUMX.js" type="text/javascript"></script>
<%= javascript_tag("signin/app") %>
# => <script src="/assets/signin/app-LSLFPUMX.js" type="text/javascript"></script>
<%= stylesheet_tag("app") %>
# => <link rel="stylesheet" href="/assets/app-LSLFPUMX.css">
Access assets programmatically via the assets component
# In console
app["assets"]["app.js"].url
# => "/assets/app-LSLFPUMX.js"
# In a view part
context.assets["default-cover-image.jpg"]
The assets component is automatically available in the view context.
Inject assets as a dependency
class MyService
include Deps["assets"]
end
Configure a CDN base URL
# config/app.rb
module Bookshelf
class App < Hanami::App
environment :production do
config.assets.base_url = "https://some-cdn.net/my-site"
end
end
end
All asset helpers return absolute URLs prefixed with the CDN:
asset_url("app.js")
# => "https://some-cdn.net/my-site/assets/app-LSLFPUMX.js"
Update CSP for CDN resources
environment :production do
config.actions.content_security_policy[:script_src] += " https://some-cdn.net"
config.actions.content_security_policy[:style_src] += " https://some-cdn.net"
config.assets.base_url = "https://some-cdn.net/my-site"
end
Enable Subresource Integrity (SRI)
environment :production do
config.assets.subresource_integrity = [:sha256, :sha512]
end
Generates tags with integrity and crossorigin attributes:
<script
src="/assets/app-LSLFPUMX.js"
type="text/javascript"
integrity="sha256-WB2pRuy8LdgAZ0aiFxLN8DdfRjKJTc4P4xuEw31iilM="
crossorigin="anonymous"
></script>
Use :sha256 to start (lighter CPU cost).
Customize esbuild options
// config/assets.js
import * as assets from "hanami-assets";
await assets.run({
esbuildOptionsFn: (args, esbuildOptions) => {
// Modify esbuildOptions here
return esbuildOptions;
}
});
Apply different options for compile vs watch
await assets.run({
esbuildOptionsFn: (args, esbuildOptions) => {
if (args.watch) {
// Development options
} else {
// Production options
}
return esbuildOptions;
}
});
Customize slice-level assets
Create slices/<slice_name>/config/assets.js to override the top-level config for a specific slice.
When assisting with Hanami Assets tasks, follow this workflow:
Identify the asset needs
Set up entry points
app/assets/js/ with app.js namingGuide asset usage in views
asset_url for getting URLsjavascript_tag / stylesheet_tag for generating HTMLAddress production deployment
hanami assets compile for production buildsbase_url when neededGuide customization
esbuildOptionsFnargs.watchReview and refine
When detailed information is needed about specific topics, consult the Hanami documentation:
app.js (within a directory under js/)hanami assets compile for production, hanami assets watch for development:sha256 over :sha512 for SRI to reduce compilation timepublic/assets/<slice_name>/ (underscored)assets component is only available within the app or slice that owns the assetsapp.js for shared/global stylesHANAMI_SERVE_ASSETS=true only when needed (e.g., Docker):sha256 to protect against CDN compromisehanami dev for combined server + asset watchinghanami assets compile before deploying to productionpublic/assets/assets.json exists after compilationpublic/assets/ for debuggingesbuildOptionsFn for all esbuild customizationsargs.watchconfig/assets.js for per-slice customization