CSS & JavaScript
How to handle assets for use in a web browser
Browser assets are stored in two distinct folders, based on whether these assets require processing or not.
Static Assets
Any assets that do not require processing (e.g., fonts, robots.txt, icons) should be placed in the /public directory. They will be served as static files from the root directory.
| Route path | URL |
|---|---|
| public/robots.txt | /robots.txt |
| public/fonts/ubuntu.woff2 | /fonts/ubuntu.woff2 |
CSS & JavaScript
CSS, JavaScript, or TypeScript files that require processing and minification should be stored in the src directory and must be named as index.[css|js|jsx|ts|tsx]. Only index files serve as bundle entries and are accessible to the client. This approach offers flexibility in creating a custom directory layout.
Please note: Jeasx doesn't specify any hardcoded outbase directory for esbuild, so it defaults to the lowest common ancestor directory for all your browser assets. So when you put all your assets into a dedicated folder (e.g. src/browser), browser will be removed from the resulting path.
| Route path | URL |
|---|---|
| src/index.css | /index.css |
| src/custom/index.js | /custom/index.js |
| src/utils/date.js | This file will be not available via an url. |
Please note: as JavaScript or TypeScript is compiled to ECMAScript modules (ESM) via esbuild, you should add type="module" to your script tags to avoid subtle errors.
Intercept static assets with reply.file
All static files - whether stored in the public directory or the dist folder - are delivered via the Jeasx route pipeline immediately following the guard processing stage. This architecture allows you to intercept static file requests seamlessly. You can access static file metadata directly within guards via reply.file. If a file exists at the requested path, this object is pre-populated with headers, statusCode, and stream. This enables you to apply or bypass authentication, modify headers, or replace content on the fly.
export default async function ({ request, reply }) {
if (reply.file) {
console.log(reply.file.statusCode, reply.file.headers, reply.file.stream);
// Delete header
delete reply.file.headers["ETag"];
// Overwrite existing header
reply.file.headers["Cache-Control"] = "public, no-cache";
}
// Don't respond with static "/robots.txt"
if (request.path === "/robots.txt") {
reply.file = undefined;
}
}