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

@zotezica/simple-front

v0.1.0

Published

Minimal JS library for server-rendered apps with interactive islands

Downloads

93

Readme

@zotezica/simple-front

A minimal JS library for server-rendered apps with interactive islands.

Philosophy

Modern full-stack frameworks (Next.js, SvelteKit, Remix) own everything — routing, data fetching, rendering — and couple your backend to their conventions. This is too much when you have a real backend (Hono, Express, Rails) that renders HTML and you just need a little client-side interactivity.

The insight: the only things you actually need from a framework are:

  1. Smooth navigation between server-rendered pages (no full-page blink)
  2. A way to mount interactive components onto server-rendered DOM nodes

That's it. Everything else stays on the server.

Core principles:

  • Server renders all read-only data — no spinners, no client-side fetches for page content
  • Islands own only interactivity: forms, toggles, drawers
  • CSP script-src 'self' compliant — no inline event handlers, no eval
  • No opinions about your backend or build tool

Install

npm install @zotezica/simple-front

Svelte and React are optional peer dependencies — install whichever you use.

Usage

// Svelte
import { island, start, slideDownFadeIn } from "@zotezica/simple-front/svelte";

// React
import { island, start, slideDownFadeIn } from "@zotezica/simple-front/react";

Register islands and call start():

const animate = slideDownFadeIn();

island("search-island", () => import("./islands/Search.svelte"), { animate });

island("product-island", () => import("./islands/Product.svelte"), {
  props: (el) => ({ productId: el.dataset.productId }),
  animate,
});

start();

The server renders mount points as empty divs with data-* props:

<div id="product-island" data-product-id="abc123"></div>

simple-front finds them by ID, reads props from data-* attributes, and mounts the component.

API

island(id, importer, options?)

Registers an island.

| Option | Type | Description | |--------|------|-------------| | props | (el: HTMLElement) => Record<string, unknown> | Reads props from the element before mounting | | animate | IslandAnimation | Animation to run when the island mounts |

start()

Attaches the click interceptor, popstate, and app:navigate listeners, then mounts all registered islands found in the current DOM.

navigate(url, options?)

Programmatically navigate to a URL. Options: { replace?: boolean }.

app:navigate event

Islands trigger navigation by dispatching a custom event — no need to import navigate:

window.dispatchEvent(new CustomEvent("app:navigate", {
  detail: { url: "/products/abc123" }
}));

Animations

Built-in animations, all accepting { duration?, easing? }:

import { slideDown, fadeIn, slideDownFadeIn } from "@zotezica/simple-front/svelte";

island("my-island", () => import("./My.svelte"), {
  animate: slideDownFadeIn({ duration: 300 }),
});

| Animation | Description | |-----------|-------------| | slideDown() | Animates height from 0 to natural height | | fadeIn() | Animates opacity from 0 to 1 | | slideDownFadeIn() | Slide, then fade |

You can also pass any custom animation:

island("my-island", () => import("./My.svelte"), {
  animate: {
    prepare: (el) => { el.style.opacity = "0"; },
    run: (el) => el.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 300 }).finished,
  },
});

Navigation

simple-front intercepts same-origin link clicks, fetches the new page, diffs the DOM with morphdom, and updates the URL — no full-page reload. View Transitions API is used when available.

The X-Navigation: 1 header is sent on every fetch, letting the server skip the layout if it wants to return a partial response.