- π Blazing Fast - Leverages Bun's high-performance HTTP server and runtime
- π¦ Full SSR Support - Complete server-side rendering for Angular 19+
- ποΈ SSG/Prerendering - Generate static HTML files at build time
- π Automatic Hydration - Seamless client-side hydration
- π Static File Serving - Optimized static asset serving with compression
- πΎ Built-in Caching - LRU cache for rendered pages
- π οΈ Angular CLI Integration - Schematics for easy setup with
ng add - π§ Custom Builders - Angular CLI builders for development and production
- Angular 19.0.0 or higher
- Bun 1.0.0 or higher
- Node.js 20.0.0 or higher (for Angular CLI)
ng add @pegasusheavy/ngx-bunThis will:
- Install the package and dependencies
- Generate the server file (
server.ts) - Create server-side configuration files
- Update
angular.jsonwith SSR targets - Add npm scripts for development and production
# Install the package
pnpm add @pegasusheavy/ngx-bun
# Install peer dependencies
pnpm add @angular/ssr @angular/platform-serverng add @pegasusheavy/ngx-bunpnpm dev:ssrpnpm build:ssrpnpm serve:ssrThe generated server.ts file can be customized:
import { createBunServer, createBunAngularEngine } from '@pegasusheavy/ngx-bun';
const engine = createBunAngularEngine({
bootstrap: () => import('./src/main.server'),
browserDistFolder: './dist/my-app/browser',
// Cache configuration
enableCache: true,
maxCacheSize: 100,
cacheTtl: 300_000, // 5 minutes
});
createBunServer({
engine,
port: 4000,
hostname: 'localhost',
// Enable request logging
logging: true,
// Routes to skip SSR (client-only)
clientOnlyRoutes: ['/admin/*'],
// Development mode
development: process.env.NODE_ENV !== 'production',
});Configure how each route is rendered in app.routes.server.ts:
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
// Client-side only (no SSR)
{ path: 'admin/**', renderMode: RenderMode.Client },
// Prerendered at build time (SSG)
{ path: 'about', renderMode: RenderMode.Prerender },
{ path: 'blog/:slug', renderMode: RenderMode.Prerender },
// Server-side rendered on each request (SSR)
{ path: '**', renderMode: RenderMode.Server },
];The schematic adds these targets to your angular.json:
{
"serve-ssr": {
"builder": "@pegasusheavy/ngx-bun:serve",
"options": {
"browserTarget": "my-app:build",
"port": 4000
}
},
"prerender": {
"builder": "@pegasusheavy/ngx-bun:prerender",
"options": {
"browserTarget": "my-app:build:production",
"routes": ["/", "/about", "/contact"]
}
}
}Creates the Angular SSR engine.
interface BunAngularEngineOptions {
// Angular bootstrap function
bootstrap: () => Promise<ApplicationRef>;
// Path to browser distribution
browserDistFolder: string;
// Path to server distribution (optional)
serverDistFolder?: string;
// Custom index.html path (optional)
indexHtml?: string;
// Additional providers (optional)
providers?: Provider[];
// Enable render caching (default: true)
enableCache?: boolean;
// Max cache entries (default: 100)
maxCacheSize?: number;
// Cache TTL in ms (default: 300000)
cacheTtl?: number;
}Creates and starts the Bun server.
interface BunServerOptions {
// The Angular engine instance
engine: BunAngularEngine;
// Server port (default: 4000)
port?: number;
// Server hostname (default: 'localhost')
hostname?: string;
// Enable static file serving (default: true)
serveStatic?: boolean;
// Static files directory
staticDir?: string;
// Development mode (default: false)
development?: boolean;
// Enable request logging (default: false)
logging?: boolean;
// Routes to skip SSR
clientOnlyRoutes?: string[];
// TLS configuration for HTTPS
tls?: { cert: string; key: string };
// Server start callback
onStart?: (server: Server) => void;
}Creates a request handler for custom server setups.
import { createRequestHandler } from '@pegasusheavy/ngx-bun';
const handler = createRequestHandler({
engine,
staticHandler,
logging: true,
});
// Use with Bun.serve directly
Bun.serve({
fetch: handler,
port: 4000,
});Creates a static file handler with caching and compression.
interface StaticFileOptions {
// Root directory for static files
root: string;
// Index file name (default: 'index.html')
index?: string;
// Enable compression (default: true)
compression?: boolean;
// Custom MIME types
mimeTypes?: Record<string, string>;
// Cache control header
cacheControl?: string | ((path: string) => string);
}ng run my-app:prerenderimport { prerenderRoutes } from '@pegasusheavy/ngx-bun/prerender';
const result = await prerenderRoutes({
engineOptions: {
bootstrap,
browserDistFolder: './dist/browser',
},
routes: ['/', '/about', '/blog/post-1', '/blog/post-2'],
outputDir: './dist/prerendered',
concurrency: 5,
generateSitemap: true,
baseUrl: 'https://example.com',
onProgress: (completed, total, path) => {
console.log(`[${completed}/${total}] ${path}`);
},
});
console.log(`Prerendered ${result.success} routes in ${result.totalTime}ms`);const result = await prerenderRoutes({
routes: [
'/',
'/about',
{
path: '/blog/:slug',
getParams: async () => {
// Fetch slugs from CMS or database
const posts = await fetchBlogPosts();
return posts.map(post => ({ slug: post.slug }));
},
},
],
// ...
});| Variable | Description | Default |
|---|---|---|
NODE_ENV |
Environment mode | development |
PORT |
Server port | 4000 |
HOST |
Server hostname | localhost |
BROWSER_DIST_FOLDER |
Browser dist path | Auto-detected |
SERVER_DIST_FOLDER |
Server dist path | Auto-detected |
- Enable caching in production for frequently accessed pages
- Use prerendering for static content that doesn't change often
- Set appropriate cache headers for static assets
- Use
clientOnlyRoutesfor pages that don't benefit from SSR - Monitor render times using the
X-Render-Timeresponse header
| Feature | @pegasusheavy/ngx-bun | Express SSR |
|---|---|---|
| Cold Start | ~50ms | ~200ms |
| Memory Usage | Lower | Higher |
| Requests/sec | Higher | Lower |
| Bundle Size | Smaller | Larger |
| Native TypeScript | β | β |
Make sure you've built the browser application first:
ng buildEnsure main.server.ts exists and exports a bootstrap function:
// src/main.server.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { config } from './app/app.config.server';
const bootstrap = () => bootstrapApplication(AppComponent, config);
export default bootstrap;Ensure your components handle server/browser differences:
import { isPlatformBrowser } from '@angular/common';
import { PLATFORM_ID, inject } from '@angular/core';
@Component({...})
export class MyComponent {
private platformId = inject(PLATFORM_ID);
ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
// Browser-only code
}
}
}Contributions are welcome! Please read our Contributing Guide for details.
MIT Β© Pegasus Heavy Industries
- π Documentation
- π Issue Tracker
- π¬ Discussions