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

@chris-briddock/create-lit-app

v0.2.0

Published

Scaffold a production-ready Lit application with best practices baked in and optional SSR.

Readme

create-lit-app

CI/CD Pipeline npm

Scaffold a production-ready Lit application with best practices baked in and optional server-side rendering — what create-next-app is for React, for web components.

npm create @chris-briddock/lit-app my-app

No arguments are required; the CLI asks for what it needs. It has zero runtime dependencies, so there is nothing to download before the prompts appear.

What you get

  • Lit 3 + Vite 8 — Vite 8 bundles with Rolldown and transforms with Oxc, so builds are fast without extra configuration.
  • TypeScript 6, configured for the decorator flavour Lit and the bundler actually agree on (see Decorators).
  • Optional SSR with @lit-labs/ssr: streaming server rendering, declarative shadow DOM, and client hydration.
  • Optional routing, with an example page tree and a 404 route.
  • Optional Tailwind CSS 4, wired through the @tailwindcss/vite plugin and made to work inside shadow roots (see Tailwind and shadow DOM).
  • Vitest 4 in browser mode — component tests run in real Chromium, because shadow DOM and custom element upgrade timing are exactly what a DOM emulator approximates worst.
  • ESLint 10 + Prettier, including eslint-plugin-lit, which understands html tagged templates and catches binding mistakes a general-purpose linter cannot see.
  • A custom elements manifest for editor autocomplete and hover docs.

Every generated project passes its own lint, format:check, typecheck and build on the first run.

Usage

npm  create @chris-briddock/lit-app my-app
pnpm create @chris-briddock/lit-app my-app
yarn create @chris-briddock/lit-app my-app
bun  create @chris-briddock/lit-app my-app

The package manager you invoke is detected and used for the install and for the commands printed at the end.

Options

Every prompt has a flag, so the CLI is fully scriptable:

| Flag | Description | | --- | --- | | --typescript / --javascript | Language (default: TypeScript) | | --ssr / --spa | Rendering strategy (default: SPA) | | --router / --no-router | Routing and example pages | | --tailwind / --no-tailwind | Tailwind CSS (default: no) | | --eslint / --no-eslint | ESLint + Prettier | | --tests / --no-tests | Vitest browser tests | | --git / --no-git | Initialise a git repository | | --install / --no-install | Install dependencies | | --use-npm, --use-pnpm, --use-yarn, --use-bun | Force a package manager | | -y, --yes | Accept defaults, skip prompts | | --force | Scaffold into a non-empty directory |

# Fully non-interactive
npm create @chris-briddock/lit-app my-app -- --ssr --router --yes

# Scaffold into the current directory
npm create @chris-briddock/lit-app . --javascript --no-tests

Prompts are skipped automatically when stdin is not a TTY, so CI needs no special handling.

SSR

Choosing --ssr produces an Express server that runs Vite in middleware mode during development — so you get server rendering and hot module replacement — and serves pre-built assets in production.

npm run dev              # server-rendered, with HMR
npm run build            # type-check, then build client + server bundles
npm run start            # production server

The response is streamed: the document head is flushed before rendering begins, so the browser starts fetching CSS and JS while the server is still producing markup.

Two details the template gets right, and which are easy to get wrong by hand:

  • @lit-labs/ssr-client/lit-element-hydrate-support.js is imported before anything that imports lit. Otherwise elements discard the server-rendered shadow DOM and re-render from scratch.
  • With --ssr --router, the route table is plain data shared by the server and the browser, and the active path is passed as an attribute. Attributes survive into the HTML, so the client hydrates with the same value the server rendered. A property binding would exist only on the server.

@lit-labs/router is used for SPA routing only — it reads window on connect, so it cannot run during server rendering.

Tailwind and shadow DOM

--tailwind installs Tailwind 4 the documented way: the @tailwindcss/vite plugin, and @import 'tailwindcss' in src/styles/global.css. No config file, no PostCSS step.

That alone is not enough for Lit. Components render into a shadow root, and document stylesheets do not cross that boundary, so utility classes in a component template would silently do nothing. Generated projects bridge it with two small files:

/* src/styles/shadow.css — utilities, without the document-level reset */
@reference './global.css';
@import 'tailwindcss/utilities.css';
// src/styles/tailwind.ts
import { unsafeCSS } from 'lit';
import sheet from './shadow.css?inline';

export const tailwind = unsafeCSS(sheet);

Components then adopt it with static styles = [tailwind, css`...`], keeping the handwritten block last for what utilities cannot express — :host and ::slotted().

Three decisions behind that, each verified against a real build:

  • Preflight is excluded from the shadow sheet. It is a document-level reset; re-applying it inside every shadow root is wasteful. The cost is that components needing font: inherit on form controls, or no underline on links, restate those rules themselves — and the templates do.
  • The theme comes from @reference './global.css'. That loads the project stylesheet for its theme while emitting none of its CSS, so a token declared in its @theme block generates utilities inside components as well as in the document. Compiling the shadow sheet against Tailwind's stock theme instead would leave custom tokens working in index.html and silently doing nothing in every component. Referenced values are inlined as fallbacks, so the sheet stays self-contained rather than depending on variables reaching it from :root.
  • Light DOM is not used instead. Overriding createRenderRoot() would let global.css reach components and remove the need for both files, but @lit-labs/ssr still serialises output into a declarative shadow root. Under --ssr that yields the server's copy sealed in a shadow root plus a second copy rendered on hydration, so it only works for --spa.

A note on decorators

Generated TypeScript projects use experimentalDecorators: true with useDefineForClassFields: false.

This is deliberate and verified rather than inherited. Standard ES decorators with the accessor keyword type-check under TypeScript 6, but Vite 8 emits them untransformed — the resulting bundle contains @dec accessor x, which is a syntax error in every current JavaScript engine. Experimental decorators lower correctly to __decorate calls and honour useDefineForClassFields, which Lit's reactive properties depend on.

The generated tsconfig.json says as much, so nobody "modernises" it and ships a broken bundle.

Development

npm install
npm test                    # unit + filesystem integration tests
npm run smoke               # scaffold, install and build every combination
npm run smoke -- --quick    # TypeScript only, faster
npm run check-versions      # verify every pinned dependency resolves
npm run format:templates    # keep templates format-clean

npm test is offline and fast. npm run smoke hits the network and is the check that matters before publishing.

How templates work

Templates are composed in ordered layers, each copied over the last:

base → lang/<ts|js> → app/<spa|ssr> → app/<spa|ssr>-<lang>
     → router/<rendering>-<lang>
     → tailwind/base → tailwind/lang/<lang> → tailwind/app/<rendering>-<lang>
     → tailwind/router/<rendering>-<lang>
     → tooling/eslint-<lang> → tooling/test-<lang>

A later layer overrides a single file from an earlier one — routing replaces app-root, for instance — which avoids needing a template per combination of answers.

Within a layer:

  • *.tpl files are rendered with {{variable}} interpolation and {{#if flag}} / {{#unless flag}} blocks, and lose the suffix.
  • _gitignore, _prettierrc and friends are renamed to their dotfile form, because npm refuses to publish files named .gitignore.

package.json is built in code (src/manifest.ts) rather than templated, since its dependencies and scripts vary along all four axes at once. All versions are pinned in one place, src/versions.ts.

Requirements

Node.js 20.19+ or 22.12+, matching Vite 8's supported range.

License

MIT