@inglorious/ssx
v1.6.4
Published
Server-Side-X. Xecution? Xperience? Who knows.
Maintainers
Readme
@inglorious/ssx
Static Site Xecution - Build blazing-fast static sites with @inglorious/web, complete with server-side rendering, client-side hydration, and zero-config routing.
SSX takes your entity-based web apps and generates optimized static HTML with full hydration support. Think Next.js SSG or Astro, but with the simplicity and predictability of Inglorious Web's entity architecture.
Why SSX?
⚡️ Fast by Default
- Pre-rendered HTML - Every page is built at compile time
- Instant load times - No waiting for server responses
- CDN-ready - Deploy anywhere static files are served
- Perfect Lighthouse scores - SEO and performance out of the box
🎯 Simple Architecture
- No server required - Pure static files
- No complex build configs - Convention over configuration
- File-based routing - Pages are just files in
src/pages/ - Entity-based state - Same familiar patterns from @inglorious/web
🔥 Modern DX
- Hot reload dev server - See changes instantly
- Lazy-loaded routes - Code splitting automatically
- lit-html hydration - Interactive UI without the bloat
- TypeScript ready - Write your pages and entities in TypeScript
- Image optimization - Automatic compression for static assets
- Markdown support - Built-in support for
.mdpages with code highlighting and math
🚀 Production Ready
- Automatic code splitting - Per-page bundles
- Optimized builds - Minified, tree-shaken output
- Source maps - Debug production like development
- Error boundaries - Graceful failure handling
Quick Start
Installation
npm install @inglorious/ssx @inglorious/webCreate Your First Site
npx @inglorious/create-app my-site --template ssx-js
cd my-site
npm run devOr manually:
TypeScript Example
// src/pages/index.ts
import { html } from "@inglorious/web"
// You can import API for type safety, though it's optional
// import type { API } from "@inglorious/web"
export const index = {
render(/* entity: any, api: API */) {
return html`
<div>
<h1>Welcome to SSX!</h1>
<p>This page was pre-rendered at build time.</p>
<nav>
<a href="/about">About</a>
</nav>
</div>
`
},
}JavaScript Example
// src/pages/index.js
import { html } from "@inglorious/web"
export const index = {
render() {
return html`
<div>
<h1>Welcome to SSX!</h1>
<p>This page was pre-rendered at build time.</p>
<nav>
<a href="/about">About</a>
</nav>
</div>
`
},
}
export const metadata = {
title: "Home",
meta: {
description: "Welcome to our site",
"og:image": "/og-image.png",
},
}Development
npm run dev
# → Dev server at http://localhost:3000Build
npm run build
# → Static site in dist/Deploy
npm run preview
# → Preview production buildDeploy dist/ to:
- Vercel - Zero config
- Netlify - Drop folder
- GitHub Pages - Push and done
- Cloudflare Pages - Instant edge
- Any CDN - It's just files!
Features
🗺️ Sitemap & RSS Generation
SSX automatically generates sitemap.xml and rss.xml based on your pages. Configure them in src/site.config.js:
export default {
// Basic metadata
title: "My Awesome Site",
meta: {
description: "A site built with SSX",
"og:type": "website",
"og:site_name": "My Site",
},
// Sitemap configuration
sitemap: {
hostname: "https://myblog.com",
filter: (page) => !["/admin", "/draft-*", "/test"].includes(page.pattern),
defaults: {
changefreq: "weekly",
priority: 0.5,
},
},
// RSS configuration
rss: {
title: "My Blog",
description: "Latest posts from my blog",
link: "https://myblog.com",
feedPath: "/feed.xml",
language: "en",
copyright: "© 2026 My Blog",
maxItems: 10,
filter: (page) => page.path.startsWith("/posts/"),
},
}Pages with a published date in metadata are included in RSS feeds.
📁 File-Based Routing
Your file structure defines your routes:
src/pages/
├── index.js → /
├── about.js → /about
├── blog.js → /blog
└── posts/
└── _slug.js → /posts/:slugDynamic routes use underscore prefix: _id.js, _slug.js, etc.
⚛️ Entity-Based State and Behavior
// src/pages/about.js
import { html } from "@inglorious/web"
export const about = {
click(entity) {
entity.name += "!"
},
render(entity, api) {
return html`<h1>
About
<span @click=${() => api.notify(`#${entity.id}:click`)}
>${entity.name}</span
>
</h1>`
},
}// src/store/entities.js
export const entities = {
about: {
type: "about",
name: "Us",
},
}🔄 Data Loading
Load data at build time with the load export:
// src/pages/blog.js
import { html } from "@inglorious/web"
export const blog = {
render(entity) {
return html`
<h1>Blog Posts</h1>
<ul>
${entity.posts?.map(
(post) => html`
<li>
<a href="/posts/${post.id}">${post.title}</a>
</li>
`,
)}
</ul>
`
},
}
// SSR: Load data during build
export async function load(entity) {
const response = await fetch("https://api.example.com/posts")
entity.posts = await response.json()
}
export const metadata = {
title: "Blog",
}The load function runs on the server during build. Data is serialized into the HTML and available immediately on the client.
🎨 Dynamic Routes with staticPaths
Generate multiple pages from data:
// src/pages/posts/_slug.js
import { html } from "@inglorious/web"
export const post = {
render(entity) {
return html`
<article>
<h1>${entity.post.title}</h1>
<div>${entity.post.body}</div>
</article>
`
},
}
// Load data for a specific post
export async function load(entity, page) {
const response = await fetch(
`https://api.example.com/posts/${page.params.slug}`,
)
entity.post = await response.json()
}
// Tell SSX which pages to generate
export async function staticPaths() {
const response = await fetch(`https://api.example.com/posts`)
const posts = await response.json()
return posts.map((post) => ({
params: { slug: post.slug },
path: `/posts/${post.slug}`,
}))
}
export const metadata = (entity) => ({
title: entity.post?.title ?? "Post",
meta: {
description: entity.post?.excerpt,
},
})📄 Page Metadata
Export metadata for HTML <head>. The metadata export can be a plain object or a function:
export const index = {
render() {
return html`<h1>Home</h1>`
},
}
// Static metadata
export const metadata = {
title: "My Site",
meta: {
description: "An awesome static site",
"og:image": "/og-image.png",
},
}
// Or dynamic metadata (uses entity data)
export const metadata = (entity) => ({
title: `${entity.user.name}'s Profile`,
meta: {
description: entity.user.bio,
"og:image": entity.user.avatar,
},
})🔥 Client-Side Hydration
Pages hydrate automatically with lit-html. Interactivity works immediately:
export const counter = {
click(entity) {
entity.count++
},
render(entity, api) {
return html`
<div>
<p>Count: ${entity.count}</p>
<button @click=${() => api.notify(`#${entity.id}:click`)}>
Increment
</button>
</div>
`
},
}The HTML is pre-rendered on the server. When JavaScript loads, lit-html hydrates the existing DOM and wires up event handlers. No flash of unstyled content, no duplicate rendering.
🧭 Client-Side Navigation
After hydration, navigation is instant:
// Links navigate without page reload
;<a href="/about">About</a> // Client-side routing
// Programmatic navigation
api.notify("navigate", "/posts")
// With options
api.notify("navigate", {
to: "/posts/123",
replace: true,
})Routes are lazy-loaded on demand, keeping initial bundle size small.
🖼️ Image Optimization
SSX includes built-in image optimization using vite-plugin-image-optimizer.
- Automatic compression - PNG, JPEG, GIF, SVG, WebP, and AVIF are compressed at build time
- Lossless & lossy - Configurable settings via
viteconfig insite.config.js
📝 Markdown Support
SSX treats .md files as first-class pages. You can create src/pages/post.md and it will be rendered automatically.
- Frontmatter - Metadata is exported as
metadata - Code highlighting - Built-in syntax highlighting with
highlight.js - Math support - LaTeX support via
katex(use$E=mc^2$or$$...$$) - Mermaid diagrams - Use
mermaidcode blocks (requires client-side mermaid.js)
Configure the syntax highlighting theme in site.config.js:
export default {
markdown: {
theme: "monokai", // default: "github-dark"
},
}Example markdown file:
---
title: My Post
---
# Hello World
This is a markdown page.CLI
SSX provides a simple CLI for building and developing:
ssx build
Builds your static site:
pnpm ssx build [options]
Options:
-c, --config <file> Config file (default: "site.config.js")
-r, --root <dir> Source root directory (default: "src")
-o, --out <dir> Output directory (default: "dist")
-i, --incremental Enable incremental builds (default: true)
-f, --force Force clean build, ignore cachessx dev
Starts the Vite development server on port 3000 with hot reload:
pnpm ssx dev [options]
Options:
-c, --config <file> Config file (default: "site.config.js")
-r, --root <dir> Source root directory (default: "src")
-p, --port <port> Dev server port (default: 3000)preview
Serves the built static site on port 3000 through the serve NPM package:
pnpm previewProject Structure
my-site/
├── src/
│ ├── pages/ # File-based routes
│ │ ├── index.js # Home page
│ │ ├── about.js # About page
│ │ └── posts/
│ │ ├── index.js # /posts
│ │ └── _id.js # /posts/:id
│ ├── store/ # Store configuration
│ │ └── entities.js # Entity definitions
│ └── types/ # Custom entity types (optional)
├── dist/ # Build output
├── package.json
└── site.config.js # Site configurationComparison to Other Tools
| Feature | SSX | Next.js (SSG) | Astro | Eleventy | | ------------------ | ----------- | ------------- | ------ | -------- | | Pre-rendered HTML | ✅ | ✅ | ✅ | ✅ | | Client hydration | ✅ lit-html | ✅ React | ✅ Any | ❌ | | Client routing | ✅ | ✅ | ✅ | ❌ | | Lazy loading | ✅ | ✅ | ✅ | ❌ | | Entity-based state | ✅ | ❌ | ❌ | ❌ | | Zero config | ✅ | ❌ | ❌ | ❌ | | Framework agnostic | ❌ | ❌ | ✅ | ✅ |
SSX is perfect if you:
- Want static site performance
- Love entity-based architecture
- Prefer convention over configuration
- Need full client-side interactivity
- Don't want React/Vue lock-in
Advanced Usage
Site Configuration
Customize SSX behavior in src/site.config.js:
export default {
// Basic metadata
lang: "en",
charset: "UTF-8",
title: "My Awesome Site",
meta: {
description: "A site built with SSX",
"og:type": "website",
},
// Global assets
styles: ["./styles/reset.css", "./styles/theme.css"],
scripts: ["./scripts/analytics.js"],
// Build options
basePath: "/",
rootDir: "src",
outDir: "dist",
publicDir: "public",
favicon: "/favicon.ico",
// Router config
router: {
trailingSlash: false,
scrollBehavior: "smooth",
},
// Vite config passthrough
vite: {
server: {
port: 3000,
open: true,
},
},
// Build hooks
hooks: {
beforeBuild: async (config) => console.log("Starting build..."),
afterBuild: async (result) => console.log(`Built ${result.pages} pages`),
},
}Environment Variables
Use Vite's environment variables:
// Access in your code
const apiUrl = import.meta.env.VITE_API_URL
// .env file
VITE_API_URL=https://api.example.comCustom 404 Page
Create a fallback route:
// src/pages/404.js
export const notFound = {
render() {
return html`
<div>
<h1>404 - Page Not Found</h1>
<a href="/">Go Home</a>
</div>
`
},
}
export const metadata = {
title: "404",
}Register it in your router:
// src/store/entities.js
import { setRoutes } from "@inglorious/web/router"
setRoutes({
// ... other routes
"*": "notFound", // Fallback
})Incremental Builds
SSX enables incremental builds by default. Only changed pages are rebuilt, dramatically speeding up your build process:
ssx build
# Only changed pages are rebuilt
ssx build --force
# Force a clean rebuild of all pagesIncremental builds respect your page dependencies and invalidate the cache when dependencies change.
Component Compatibility
Fully Supported
- All Inglorious Web components (
table,list,select,form) - Custom components using lit-html templates
- Plain HTML and CSS
Limited Support
- Third-party Web Components (Shoelace, Material Web, etc.)
- Will not appear in pre-rendered HTML
- Require client-side JavaScript to initialize
- Best used for client-only interactive features
- Consider using Inglorious Web components for SSG content
API Reference
Build API
import { build } from "@inglorious/ssx/build"
await build({
rootDir: "src",
outDir: "dist",
configFile: "site.config.js",
incremental: true,
clean: false,
})Dev Server API
import { dev } from "@inglorious/ssx/dev"
await dev({
rootDir: "src",
port: 3000,
configFile: "site.config.js",
})Roadmap
- [x] TypeScript support
- [x] Image optimization
- [ ] API routes (serverless functions)
- [x] Markdown support
- [ ] i18n helpers
Philosophy
SSX embraces the philosophy of @inglorious/web:
- Simplicity over cleverness - Obvious beats clever
- Convention over configuration - Sensible defaults
- Predictability over magic - Explicit is better than implicit
- Standards over abstractions - Use the platform
Static site generation should be simple. SSX makes it simple.
Contributing
Contributions are welcome! Please read our Contributing Guidelines first.
License
MIT License - Free and open source
Created by Matteo Antony Mistretta
Related Packages
- @inglorious/web - Entity-based web framework
- @inglorious/store - State management
- @inglorious/engine - Game engine
Support
Build static sites the Inglorious way. Simple. Predictable. Fast. 🚀
