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

@jxsuite/starters

v1.7.0

Published

Starter site templates for new Jx projects

Downloads

1,975

Readme

@jxsuite/starters

The catalogue of ready-made starter sites a user can clone when creating a new Jx project, either from Studio's New Project picker or with bun create @jxsuite <directory> --template <id>. The package is two things: registry.json, the picker metadata, and sites/, one complete buildable Jx project tree per starter.

The TypeScript module itself is metadata-only. It reads the registry and resolves a starter's directory; the copy-and-rewrite of a clone happens in @jxsuite/create's generate.ts.

API

import { getStarter, getStarterDir, listStarters, SITES_DIR } from "@jxsuite/starters";

listStarters(); // StarterMeta[] — parsed once, memoized (same array instance)
getStarter("restaurant"); // StarterMeta | undefined
getStarterDir("restaurant"); // <SITES_DIR>/restaurant; throws on an unknown id

SITES_DIR is join(import.meta.dirname, "sites"), so the same code resolves from the monorepo and from the copy staged inside the packaged desktop app. getStarterDir throws Unknown starter: "<id>" rather than handing back a path that does not exist. The registry is also exported raw as @jxsuite/starters/registry.json, for consumers that want the array without the module. The package publishes raw TypeScript: exports["."] is ./index.ts, there is no build step, and the module imports only node:fs and node:path.

The registry contract

registry.json is a flat JSON array. Every entry carries all eight fields; there are no optional keys and no defaults (index.ts, StarterMeta):

| Field | Contract | | ------------- | ------------------------------------------------------------ | | id | Unique; also the sites/<id> directory name | | name | Display name in the picker | | industry | Archetype label, e.g. Restaurant & Food | | tagline | One line under the name | | description | Longer copy for the showcase | | features | Non-empty string[] of Jx features the starter demonstrates | | accent | Hex colour, /^#[0-9a-fA-F]{3,8}$/ | | thumbnail | data:image/… URI (generated, see below) |

tests/registry.test.ts asserts each of those, that ids are unique, and that every id maps to a sites/<id>/project.json. Never restate the number of starters in prose: scripts/docs/check-site-claims.ts reads registry.json and fails the root README.md or any sites/jxsuite.com/pages/** page claiming a different count (digits and number words alike). The same gate holds the templates page's ::starter-card slugs to the registry ids, both directions, so a starter added without a card is caught too. It does not scan this file, so here the habit is the only guard.

The registry is the source several unrelated gates read by path: the docs generator, the marketing-claims count check, the packaged-desktop bundle verifier, and the screenshot lane's shot selector.

Layout of sites/

Each sites/<id>/ is a complete, buildable Jx project. It contains components/, layouts/, pages/, content/, public/, project.json, a package.json, and two committed generated entry schemas:

sites/restaurant/
  project.json           # validated by parseProjectConfig and by its own bundle
  project.schema.json    # generated by `bun run schema:generate-all`
  document.schema.json   # generated by `bun run schema:generate-all`
  components/ layouts/ pages/ content/ public/
  package.json           # PUBLISHED @jxsuite/* ranges; rewritten by release-please
  images.json            # OPTIONAL: authoring-only Pexels manifest, stripped from clones
  .gitignore             # .cache, dist, node_modules

tests/validate.test.ts holds the tree to its schemas: the two committed bundles must be self-contained (no relative $refs survive), project.json must satisfy both parseProjectConfig and its own project.schema.json, every {components,layouts,pages}/**/*.json must validate against document.schema.json (and at least one must exist), and every content/**/*.md must start with frontmatter. tests/validate-projects.test.ts runs each starter through @jxsuite/schema's validateProjectFile as well.

Starters are not uniform by design: the museum starter is the multilingual one (i18n.locales: ["en", "fr-CA", "ar"] with routing: "prefix-except-default"), and real-estate's listings collection uses format: "Csv" where the others use Markdown.

Consumption: cloned, never depended on

Nothing a user ships depends on this package. @jxsuite/create imports getStarterDir lazily (so blank projects never load the large starters package), copies the tree, and then re-stamps identity:

bun create @jxsuite my-site --template restaurant
  • Excluded from the copy: node_modules, dist, .cache, .jx-cache, .git, and images.json (packages/create/generate.ts).
  • project.json is preserved, with only name / url / description / build.adapter re-stamped (plus design tokens when the quickstart supplies them). Content, components, layouts and public assets are copied verbatim.
  • package.json is rebuilt from scratch with current dependency ranges, so a clone never inherits whatever the in-repo starter pinned.
  • --template resolves built-in template ids first, then registry starter ids; an unknown id falls back to blank.

Two other consumers read the registry live: the dev server exposes it to Studio's picker at GET /__studio/starters, returning listStarters() verbatim (specs/server.md §4.1), and the desktop app imports listStarters directly in both its Electrobun and Chromium window layers.

Authoring or modifying a starter

  1. Add the sites/<id>/ tree and a registry.json entry with all eight fields.

  2. bun run schema:generate-all regenerates every project.schema.json and document.schema.json. Always via this script, never by hand: it runs schema:clean-roots first (see below). bun run schema:verify proves the result in CI, so a stale entry schema is a red build. .github/workflows/schemas.yml also regenerates it and pushes the fix to your branch, with a comment naming the JSON Pointers that moved.

  3. bun run docs:generate, because docs/studio/projects/starters.md is rendered from registry.json by scripts/docs/generators/starters.ts. Hand-editing it is a bug; CI diffs the page.

  4. bun run screenshots:thumbnails. The thumbnail data URI is not hand-authored. The script builds the starter with image optimization off, serves dist/, screenshots the hero viewport in headless Chromium, and writes the small JPEG into registry.json plus a full-res JPEG into sites/jxsuite.com/public/starters/<id>.jpg. (This is why registry.json is large.)

  5. Photos, if the starter ships any:

    PEXELS_API_KEY=… bun packages/starters/scripts/fetch-photos.ts <site-id> [--force]

    images.json holds { site, thumbnail?, images }, where each image is { role, query, orientation?, photoId? }. The pinned photoId makes re-runs fetch the same images; --force re-picks even pinned ones. The script upserts a per-site block in CREDITS.md between <!-- site:<id>:start --> and <!-- site:<id>:end -->. Committed output is plain image files, so scaffolded projects have no Pexels or network dependency.

  6. bun test --isolate from packages/starters. Per-file coverage is gated at 99% lines / 99% functions (bunfig.toml); sites/, scripts/ and tests/ are excluded from coverage.

Adding a starter needs no desktop config change: packages/desktop/scripts/verify-bundle.ts derives its checklist from the staged registry, so every new id is covered automatically.

Hazards

The shadowed core. A starter's package.json pins published @jxsuite/* ranges. It must, because it is a template a user scaffolds from, and starters are not bun workspace members, so workspace:^ is unavailable. Iterating a starter in Studio runs bun install in that root, which materialises a real @jxsuite/schema beside a workspace far ahead of it. That has already produced a starter document schema narrower than the starter's own content, unnoticed for six weeks (scripts/check-shadowed-core.ts carries the full post-mortem). bun run schema:generate-all therefore runs schema:clean-roots (bun scripts/check-shadowed-core.ts --fix) first, removing only node_modules/@jxsuite/* and a stray lockfile. Third-party dependencies stay, because the install is what makes the starter preview, and a workspace symlink is never removed.

Never put that cleanup in a starter's package.json. This package publishes sites/, so those manifests ship to end users; a postinstall or preschema hook there would run on their machine and delete the dependencies they just installed. The monorepo has the workspace being shadowed, so the monorepo owns the cleanup.

Do not hand-edit the @jxsuite/* ranges in sites/*/package.json. release-please rewrites them inside its release commit via extra-files; the gate is bun run templates:check (templates:sync to repair).

Packaged-desktop path contract. ElectroBun inlines the bun-side JS graph, so import.meta.dirname resolves to app/bun/ at runtime. registry.json and sites/ must therefore be staged to bun/registry.json and bun/sites by build.copy in packages/desktop/electrobun.config.ts (specs/desktop.md §7.4).

The screenshot lane ignores this package's source. It watches only registry.json and the six starter roots a shot actually opens (.github/workflows/screenshots.yml); a registry change arms exactly one shot, new-project, because that modal's gallery is listStarters().

Versioning

Published to npm as @jxsuite/starters. Like every @jxsuite package, it ships TypeScript source and follows the monorepo's release train (the starters release-please component). Within the monorepo, @jxsuite/create depends on it via workspace:^. Because consumption is a one-time clone, publishing a new version never changes an existing project.