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

instio

v0.1.4

Published

The Instio ecosystem — a declarative UI framework for building interactive apps in JavaScript — inspired by Gradio.

Readme

🧵 Instio

A declarative UI DSL for JavaScript — build interactive browser apps with pure functions.

Instio lets you describe your interface and event logic in a single app.js file. Inspired by Gradio, it compiles everything into a fully client-side SPA — no server round-trips, no serialisation. Your handler functions run directly in the browser.

npm create instio@latest my-app
cd my-app
npm install
npm run dev   # → http://localhost:7860
import { instio } from "@instio/core";

async function* generate(prompt) {
  const words = `You asked: "${prompt}". Here is a response.`.split(" ");
  for (const word of words) {
    yield word + " ";
    await new Promise((r) => setTimeout(r, 40));
  }
}

const app = instio((ui) => {
  const prompt  = ui.textbox({ placeholder: "Ask anything…", lines: 1 });
  const chatbot = ui.chatbot({ height: 420 });

  ui.button({ label: "Send", variant: "primary" })
    .stream(generate)
    .from(prompt)
    .to(chatbot);
});

export default app;

Table of Contents


Why Instio?

Instio brings Gradio-style ergonomics to JavaScript — declarative inputs, outputs, buttons, layout blocks, and streaming chat — while keeping your entire app deployable as static files.

| | Typical Gradio-style stack | Instio | |---|---|---| | UI definition | Server-side blocks / config | Pure JavaScript DSL in app.js | | Event handlers | Run on a Python (or Node) server | Bundled into the browser | | Deployment | Requires a running backend | Static HTML + JS (Netlify, Vercel, S3, CDN, …) | | Streaming | Server push / websockets | Async generators in the client | | Extensibility | Custom components in one runtime | Renderer-agnostic core + pluggable renderers |

Instio is a good fit when you want:

  • A demo, prototype, or internal tool with form inputs and live outputs
  • A chat UI that streams tokens from an LLM API via fetch()
  • A calculator, dashboard, or widget gallery without maintaining a separate frontend framework boilerplate
  • Something you can ship as a static site and open in any browser

Handlers stay plain JavaScript. Sync functions, async/await, and async function* generators all work. If you need an external API, call it yourself — Instio does not proxy your events through a backend.


The Instio Ecosystem

Instio is split into focused packages that work together. You typically install all three when starting a new project; advanced users can depend on @instio/core alone when building a custom renderer.

┌─────────────────────────────────────────────────────────────────┐
│                         your app.js                             │
│   import { instio } from "@instio/core"                         │
│   export default instio((ui) => { … })                          │
└────────────────────────────┬────────────────────────────────────┘
                             │
         ┌───────────────────┼───────────────────┐
         ▼                   ▼                   ▼
  ┌─────────────┐    ┌──────────────┐    ┌─────────────┐
  │ @instio/core│    │@instio/svelte│    │   instio    │
  │  DSL engine │───▶│   renderer   │◀───│  CLI + tmpl │
  │  no DOM dep │    │  Vite + SPA  │    │ npm create  │
  └─────────────┘    └──────────────┘    └─────────────┘

@instio/core — DSL engine

The heart of Instio. Provides:

  • instio() — declare your UI tree and wire events
  • ui.* components — textbox, number, button, row, column, chatbot, …
  • Event model.click(), .change(), .submit(), .stream()
  • Renderer-agnostic output{ tree, events, handlers, state }

No DOM, no bundler, no framework dependency. Use it anywhere you need to compile an Instio app description.

npm install @instio/core

@instio/svelte — Svelte renderer

The official browser renderer. Takes your app.js, bundles it with Vite, and mounts a Svelte SPA where:

  • Every component gets a reactive store keyed by node id
  • EventRunner calls your handler functions directly in the browser
  • Streaming generators update outputs on each animation frame
  • A built-in dark theme and component library ship out of the box
npm install @instio/svelte

Scaffolded apps call appRuntime() from this package to start dev and production builds.

instio — CLI & project template (this package)

The front door to Instio. One command scaffolds a working project with @instio/core and @instio/svelte pre-wired:

npm create instio@latest my-app
# also: pnpm create instio   |   yarn create instio

The default template includes a calculator demo in src/app.js — a practical reference for grid layout, button variants, and shared handler state.

| Package | npm | Role | |---------|-----|------| | @instio/core | npm | DSL — instio(), components, events, state | | @instio/svelte | npm | Svelte renderer — Vite SPA, client-side execution | | instio | npm | CLI scaffolder — npm create instio@latest <app> |

Source code and deep-dive docs for each package live on GitHub:


How It Works

src/app.js              your UI tree + handler functions
       │
       ▼  @instio/svelte bundles app.js at build time (Vite)
dist/                   static HTML + JS + CSS
       │
       ▼  open in any browser
User action  →  EventRunner calls handler()  →  output stores update instantly
  1. instio((ui) => { … }) builds a node tree, event bindings, and a live handlers map.
  2. @instio/svelte imports your app.js and renders it with Svelte components.
  3. On every click, change, or submit, EventRunner reads input stores, calls your function, writes output stores — all client-side.

The default export shape consumed by the renderer:

{ tree, events, handlers, state }

External services (LLMs, REST APIs, WebSockets) are called from your handler code — Instio never sits in the middle.


Quick Start

Prerequisites

| Tool | Version | |------|---------| | Node.js | ≥ 18.0.0 |

Create a new app

npm create instio@latest my-app
cd my-app
npm install
npm run dev      # → http://localhost:7860

Edit src/app.js — the dev server hot-reloads on save.

Build & serve

npm run build    # output → dist/
npm run serve    # serve dist/ locally

Upload dist/ to any static host. No Node.js server is required at runtime.

Scaffolded project layout

my-app/
├── index.js              dev/build entry — calls appRuntime() from @instio/svelte
├── package.json
├── README.md
├── .gitignore
├── dist/                 build output (created by npm run build)
└── src/
    ├── app.js            your Instio app — start here
    └── assets/
        └── images/       static images (referenced by key in branding)

App scripts

| Command | Description | |---------|-------------| | npm run dev | Hot-reloading Vite dev server at http://localhost:7860 | | npm run build | Production static build written to dist/ | | npm run serve | Serve dist/ with npx serve |

CLI app name rules

App names must start with a lowercase letter or digit and contain only lowercase letters, numbers, hyphens, and underscores.

Valid: my-chat-app, llm_demo, app1
Invalid: MyApp, my app, .hidden


Writing an App

Every Instio app follows the same three-step pattern:

import { instio } from "@instio/core";

// 1. Plain JS handlers — sync, async, or async generators
function greet(name) {
  return `Hello, ${name}!`;
}

// 2. Declare UI + wire events
const app = instio({
  branding: {
    name: "My App",
    logo: { key: "logo", alt: "Logo", lazy: true },
  }
}, (ui) => {

  const name   = ui.textbox({ label: "Name", placeholder: "World" });
  const output = ui.markdown({ value: "" });

  ui.button({ label: "Greet", variant: "primary" })
    .click(greet, [name], [output]);
});

// 3. Export for the renderer
export default app;

Layout blocks

Nest components inside rows and columns:

ui.row(() => {
  ui.column(() => {
    ui.number({ label: "A", value: 0 });
  });
  ui.column(() => {
    ui.number({ label: "B", value: 0 });
  }, { scale: 2 });   // twice as wide as sibling columns
});

Wiring inputs to outputs

Component handles returned by ui.* calls are wired explicitly:

const a   = ui.number({ label: "A", value: 0 });
const b   = ui.number({ label: "B", value: 0 });
const out = ui.number({ label: "Result", readonly: true });

ui.button({ label: "Add", variant: "primary" })
  .click((x, y) => Number(x) + Number(y), [a, b], [out]);

Seamless Groups

You can group form inputs together and strip out their inner spacing and border-radii using the seamless property on a group, creating a single unified block:

ui.group(() => {
  ui.textbox({ placeholder: "Search..." });
  ui.button({ label: "Go" });
}, { seamless: true });

Events

Every interactive component handle exposes a fluent event API.

Click / Change / Submit

handle.click(handler, [inputs], [outputs]);
handle.change(handler, [inputs], [outputs]);
handle.submit(handler, [inputs], [outputs]);

Handlers receive current input values in order and return an output value, or an array for multiple outputs:

function add(a, b) {
  return Number(a) + Number(b);
}

function split(text) {
  return [text.toUpperCase(), text.length];
}

function reset() {
  return "0";
}

btn.click(reset, [], [display]);   // no inputs needed

Dynamic Property Updates

Handlers can return instio.update() to dynamically change a component's properties (like visibility, label, or variant) in addition to its value, instead of just returning raw values:

import { instio, update } from "@instio/core";

function submit() {
  return [
    update({ value: "Saved!", label: "Done" }),
    update({ visible: false })
  ];
}

Errors & Toast Notifications

If a handler throws an error, Instio automatically catches it, halts execution, and displays a sliding Toast Notification at the bottom right of the screen. The error is also logged to the console.

Automatic Loading States

When an event starts, Instio automatically overlays a .tio-loading spinner and blur effect on all components marked as outputs. This prevents user interaction until the handler completes.

Streaming (async generators)

Use .stream() for progressive / token-by-token output:

ui.button({ label: "Generate", variant: "primary" })
  .stream(asyncGenerator)
  .from(prompt)
  .to(chatbot);

Each yield updates the output store immediately — ideal for LLM streaming:

async function* generate(prompt) {
  const res = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "gpt-4o",
      stream: true,
      messages: [{ role: "user", content: prompt }],
    }),
  });
  for await (const chunk of parseSSE(res.body)) yield chunk;
}

Simulated streaming without an API:

async function* generate(prompt) {
  const words = `You asked: "${prompt}".`.split(" ");
  for (const word of words) {
    yield word + " ";
    await new Promise((r) => setTimeout(r, 50));
  }
}

Component Library

Components below are registered in @instio/core and rendered by @instio/svelte.

Inputs

| Component | DSL call | Key props | |-----------|----------|-----------| | Text box | ui.textbox({ … }) | label, placeholder, value, lines, readonly | | Number | ui.number({ … }) | label, value, minimum, maximum, step, readonly | | Slider | ui.slider({ … }) | label, minimum, maximum, step, value | | Dropdown | ui.dropdown({ … }) | label, choices, value, multiselect | | Image | ui.image({ … }) | label, type, sources, height, width |

Outputs & Interactive

| Component | DSL call | Key props | |-----------|----------|-----------| | Markdown | ui.markdown({ … }) | value, sanitize_html | | JSON | ui.json({ … }) | label, value | | Dataframe | ui.dataframe({ … }) | label, value, headers | | Checkbox | ui.checkbox({ … }) | label, value | | Checkbox Group | ui.checkboxgroup({ … }) | label, value, choices | | Toggle | ui.toggle({ … }) | label, value | | Datetime | ui.datetime({ … }) | label, value, type | | File Upload | ui.file({ … }) | label, value, accept | | Image Gallery | ui.gallery({ … }) | label, value | | Progress Bar | ui.progress({ … }) | label, value | | Chatbot | ui.chatbot({ … }) | label, value, height, show_copy_button | | Button | ui.button({ … }) | label, variant (primary | secondary | stop) |

ui.clear_button() and ui.submit_button() are aliases of ui.button().

Button variants map to visual styles in the Svelte renderer:

| Variant | Typical use | |---------|-------------| | primary | Main actions, operators, submit | | secondary | Neutral actions, number keys | | stop | Clear, cancel, destructive actions |

Layout & Chrome

| Block | DSL call | Key props | |-------|----------|-----------| | Row | ui.row(() => { … }) | equal_height, variant | | Column | ui.column(() => { … }) | scale, min_width, variant |

The Svelte renderer also includes components like Checkbox, Radio, Tab, and Accordion. These are not yet exposed in the core DSL — follow the @instio/core contributor guide to add new component schemas.


State & Shared Logic

Root state object

For persistent, cross-handler state, use the state argument passed to instio():

const app = instio((ui, state) => {
  state.set({ count: 0 });

  const counter = ui.number({ label: "Count", value: 0 });
  counter.bind(state);

  ui.button({ label: "Increment" }).click(() => {
    state.set({ count: state.get().count + 1 });
  });
});

| Method | Description | |--------|-------------| | state.get() | Snapshot of current state | | state.set(partial) | Merge update and notify subscribers | | state.subscribe(fn) | Subscribe to changes; returns unsubscribe |

Module-level closures

For app-specific logic (calculator memory, multi-step wizards, game state), module-level variables work naturally — handlers are real functions bundled into the browser, not serialised strings:

const calc = { prev: null, op: null, resetNext: false };

function pressOperator(symbol) {
  return (display) => {
    calc.prev = parseFloat(display);
    calc.op = symbol;
    calc.resetNext = true;
    return display;
  };
}

Assets & Branding

Place images in src/assets/images/ and reference them by key in branding:

// Configured in instio() options:
const app = instio({
  branding: {
    name: "My App",
    logo: { key: "logo", alt: "Logo", lazy: true },
  }
}, (ui) => {
  // ...
});
// resolves src/assets/images/logo.svg at build time

The Svelte renderer indexes your asset folder via Vite and supports variant paths and density suffixes (logo@2x).


Theming & Deployment

Built-in Themes

Instio comes with several built-in themes that dramatically alter the visual appearance of your components out of the box.

You can set the theme when initializing your app:

const app = instio({ theme: "skeuomorphic" }, (ui) => { ... });

Available themes:

  • default: The classic Instio orange/neutral flat design.
  • base: A minimal, grayscale foundation theme.
  • origin: Vibrant orange, modeled after Gradio 4's appearance.
  • citrus: Amber/yellow theme with playful 3D button effects.
  • monochrome: High-contrast black and white, newspaper-like aesthetic.
  • soft: A gentle, pastel indigo theme with heavily rounded corners.
  • glass: Frosted cyan gradients with glassy blur effects (backdrop-filter).
  • ocean: Deep blue and aquatic teal gradients.
  • skeuomorphic: A deeply authentic, classic early-2000s glossy UI with inset shadows, realistic gradients, and tactile controls.

Customise the look

Global CSS custom properties are defined in @instio/svelte's theme. Override them in your own stylesheet:

:root {
  --brand-500: #6366f1;
  --brand-600: #4f46e5;
  --bg-app: #0f0f13;
  --bg-panel: #18181f;
  --bg-surface: #22222d;
  --text-primary: #f1f1f3;
  --radius-sm: 6px;
}

See the @instio/svelte theming docs for the full token list.

Deploy anywhere

npm run build

Upload the contents of dist/ to Netlify, Vercel, Cloudflare Pages, S3, GitHub Pages, or any static host. Your handlers, UI, and assets are self-contained — no Instio server required in production.


Examples

The scaffolded template ships with a calculator in src/app.js — a full demo of:

  • ui.row() / ui.column() grid layout with scale for wide keys
  • Readonly ui.textbox() as a display
  • Operator chaining, modulo, percent, and sign toggle via closure state

Other reference patterns:

Simple adder

import { instio } from "@instio/core";

function add(a, b) {
  return Number(a) + Number(b);
}

const app = instio((ui) => {
  const a   = ui.number({ label: "A", value: 0 });
  const b   = ui.number({ label: "B", value: 0 });
  const out = ui.number({ label: "Result", readonly: true });

  ui.button({ label: "Add", variant: "primary" }).click(add, [a, b], [out]);
});

export default app;

Streaming chat

import { instio } from "@instio/core";

async function* generate(prompt) {
  const words = `You asked: "${prompt}". Here is a thoughtful response.`.split(" ");
  for (const word of words) {
    yield word + " ";
    await new Promise((r) => setTimeout(r, 50));
  }
}

const app = instio((ui) => {
  ui.markdown({ value: "# 🧵 Instio Chat" });

  const chatbot = ui.chatbot({ label: "Chat", height: 480 });
  const prompt  = ui.textbox({ placeholder: "Type a message…", lines: 1 });

  ui.button({ label: "Send", variant: "primary" })
    .stream(generate)
    .from(prompt)
    .to(chatbot);
});

export default app;

More examples in the GitHub repository: basic.js · chat.js


Package Reference

When to use which package

| Goal | Install | |------|---------| | Start a new Instio app | npm create instio@latest my-app | | Add Instio to an existing bundler setup | @instio/core + @instio/svelte | | Build a custom renderer (React, Vue, …) | @instio/core only | | Run dev/build in a scaffolded app | Already included via @instio/svelte |

Manual setup (without the CLI)

npm install @instio/core @instio/svelte

Create src/app.js with your instio() definition, then wire @instio/svelte's appRuntime() in an entry file — the scaffolded template shows the exact layout.

Building a custom renderer

@instio/core is intentionally DOM-free. To build your own renderer:

  1. Import the compiled app (tree, events, handlers).
  2. Walk tree recursively — render leaf nodes and layout blocks.
  3. Maintain a value store per node id.
  4. On user interaction, call handlers with collected input values and write outputs.

Reference implementation: @instio/svelte NodeRenderer + EventRunner.


Contributing

Contributions are welcome across the entire Instio ecosystem — core DSL, Svelte renderer, CLI template, docs, and new component types.

  1. Fork the repository
  2. Create a feature branch: git checkout -b feat/my-feature
  3. Make your changes and add tests where appropriate
  4. Open a pull request

License

MIT © Instio Contributors