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

svelte-hono

v0.1.2

Published

Svelte 5 SSR + hydration inside a Hono Worker. No SvelteKit.

Readme

svelte-hono

npm version Deploy to Cloudflare MIT License

Drop a .svelte file in a Hono Worker. Real SSR. Real hydration. No SvelteKit.

import { Hono } from "hono";
import { svelteRenderer, attachSvelteRoutes } from "svelte-hono";
import { bundles } from "./bundles.generated";
import Hello from "./hello.svelte";

const app = new Hono();
attachSvelteRoutes(app, { bundles });

app.get("/", svelteRenderer(Hello, {
  hydrateAs: "hello",
  title: "Hello",
  props: { initialCount: 0 },
}));

export default app;

That's the whole worker. bundles.generated.ts is written for you by svelte-hono/build — it's a normal ES module the worker imports, no globals, no define magic.

What it does

  • Compiles your .svelte files at build time (esbuild + esbuild-svelte).
  • Server-renders Svelte 5 components on the Worker — real HTML, instant first paint.
  • Hydrates on the client — $state, $effect, event handlers, all of it.
  • Shared runtime by default. Svelte's ~103 KB client runtime ships as a single /__svelte/_runtime.<hash>.js file across every component on your site. Per-component bundles are typically 1–16 KB instead of 45–70 KB. After the first page in a session, every subsequent component is a small download.
  • Static bundles are content-hashed at build time and cached for a year on Cloudflare's edge — new deploy = new hash = new URL = guaranteed cache miss. No purging, no ?v= query strings, no stale hydration scripts after a deploy. HTML defaults to revalidate-on-every-request so deploys are visible immediately; opt into edge HTML caching via cacheControl.
  • Stays out of your way. Hono owns routing. You own the rest.
  • No eval, no new Function, no Workers codegen prohibition to dodge.

Install

npm i svelte-hono hono svelte
npm i -D esbuild esbuild-svelte wrangler

Quickstart

  1. A componenthello.svelte:

    <script>
      let { initialCount = 0 } = $props();
      let count = $state(0);
      $effect(() => { count = initialCount; });
    </script>
    <button onclick={() => count += 1}>clicks: {count}</button>
  2. A workerworker.ts (as above).

  3. A build scriptbuild.mjs:

    import { buildHonoSvelte } from "svelte-hono/build";
    
    await buildHonoSvelte({
      workerEntry: "./worker.ts",
      outDir: "./build",
      components: { hello: "./hello.svelte" },
    });
  4. A wrangler configwrangler.toml:

    name = "my-app"
    main = "build/worker.bundled.mjs"
    compatibility_date = "2026-04-21"
    compatibility_flags = ["nodejs_compat"]
    
    [build]
    command = "node build.mjs"
  5. Add to .gitignore:

    bundles.generated.ts
    build/
  6. Run it:

    npx wrangler dev

    Click the button. Counter goes up. You just rendered Svelte 5 on a Worker.

Examples in this repo

git clone https://github.com/acoyfellow/svelte-hono
cd svelte-hono
npm install && npm run build

# minimal counter
cd examples/hello && node build.mjs && npx wrangler dev

# full docs site (5 pages, multi-route, OG image, sitemap, edge-cached)
cd examples/docs && node build.mjs && npx wrangler dev

The examples/docs/ site is the live docs at svelte-hono.coey.dev. The site is built with svelte-hono itself — the same primitive you're reading about.

Caching

The whole problem with shipping a hydration script under a stable URL is that browsers and Cloudflare will happily serve the old bytes after you deploy. svelte-hono sidesteps this by content-hashing every client bundle:

| URL | Cache-Control | Who serves it | |---|---|---| | GET / (HTML) | public, max-age=0, must-revalidate (default) | Worker, every request | | GET /__svelte/{id}.{hash}.js | public, max-age=31536000, immutable | Cloudflare edge cache, indefinitely | | GET /__svelte/{id}.{hash}.css | public, max-age=31536000, immutable | Cloudflare edge cache, indefinitely |

What this buys you:

  • Deploy and walk away. New build → new hash → new URL. The HTML is fresh, references the new URL, browsers fetch fresh JS. The old URL is left behind in caches — nothing points at it, no harm done.
  • No cache purging. You never need to call the Cloudflare cache API or bust anything manually.
  • Hash mismatch → 404. If a request comes in for home.deadbeef.js and the current bundle has a different hash, the Worker returns 404 instead of silently serving the wrong bytes. Stale references fail loud.
  • HTML caching is opt-in. Set cacheControl: "public, max-age=60, s-maxage=300" on svelteRenderer to let Cloudflare cache the HTML too. Hashed asset URLs in the HTML mean even cached HTML is safe — it'll request the right JS as long as the HTML itself is still valid.

If you upgrade from a pre-hash version of svelte-hono, the legacy unhashed URL (/__svelte/home.js) still works, but is served with max-age=0, must-revalidate rather than immutable — because without a hash there's no way to make caching it safe.

Comparison

| | svelte-hono | SvelteKit + adapter-cloudflare | hono/jsx | |---|---|---|---| | Routing | Hono | SvelteKit's filesystem | Hono | | Build | One node build.mjs | Vite + adapter | None | | SSR | Yes | Yes | Yes | | Client reactivity | Yes (Svelte 5) | Yes | No | | Edge cache (CF Workers) | Yes (built-in, content-hashed) | Yes (manual) | Yes (manual) | | Conventions | None | Many | None | | Library size | ~5 KB built | n/a (full framework) | bundled with Hono |

Status

Shipping. Three exports, one build helper, content-hashed and edge-cached. The docs site is the receipt. See CHANGELOG / npm for the latest version.

License

MIT — see LICENSE.