npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@inglorious/ssx

v1.6.4

Published

Server-Side-X. Xecution? Xperience? Who knows.

Readme

@inglorious/ssx

NPM version
License: MIT

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 .md pages 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/web

Create Your First Site

npx @inglorious/create-app my-site --template ssx-js
cd my-site
npm run dev

Or 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:3000

Build

npm run build
# → Static site in dist/

Deploy

npm run preview
# → Preview production build

Deploy 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/:slug

Dynamic 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 vite config in site.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 mermaid code 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 cache

ssx 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 preview

Project 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 configuration

Comparison 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.com

Custom 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 pages

Incremental 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


Support


Build static sites the Inglorious way. Simple. Predictable. Fast. 🚀