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

aided-core

v1.3.0

Published

A minimal JavaScript library for building user interfaces with fine-grained reactivity.

Readme

Aided

Aided is a minimal JavaScript library for building user interfaces with fine-grained reactivity. It updates the DOM directly without using a Virtual DOM, focusing on performance, simplicity, and an excellent developer experience.

Core Principles

  1. Fine-Grained Reactivity: A system of reactive primitives (signals, effects, memos) ensures that when state changes, only the specific code that depends on it is re-executed.
  2. Direct DOM Manipulation: Instead of computing and diffing virtual trees, Aided's effects are bound directly to DOM nodes, enabling precise, surgical updates.
  3. Render-Once Components: Components are plain JavaScript functions that execute once to create a DOM tree and set up reactive bindings. They do not re-render.
  4. Automatic Memory Management: An ownership graph, managed via createRoot, tracks all nested reactive scopes and automatically cleans them up to prevent memory leaks.
  5. Headless Logic: Complex UI logic (like virtualization) is separated into headless, framework-agnostic utilities, promoting reusability and clean component architecture.

Installation

yarn add aided-core
# or
npm install aided-core

Getting Started

Aided uses a hyperscript function, h, for a declarative and readable way to create DOM elements. It feels like JSX, but it's just plain JavaScript functions.

index.html

<div id="app"></div>
<script type="module" src="./main.js"></script>

main.js

import { render, createSignal, h } from "aided-core";

function Counter() {
  const [count, setCount] = createSignal(0);

  // Use the `h` helper to build your UI
  return h.button(
    {
      // Event handlers are passed as props
      onClick: () => setCount(count() + 1),
    },
    // Reactive children are automatically updated
    "Count: ",
    count,
  );
}

// Mount the component to the DOM
render(Counter, document.getElementById("app"));

Interactive Playground

Explore Aided's capabilities with our comprehensive interactive playground located in the playground/ directory. The playground includes:

  • Live Examples: Interactive demos of all major features including signals, effects, memos, and components
  • Real-World Patterns: Complete implementations of common UI patterns like forms, modals, notifications, and virtualized lists
  • Component Showcase: Working examples of all structural components (For, Show, VirtualFor, etc.)
  • Advanced Patterns: Demonstrations of complex reactivity patterns including context, portals, and state isolation
  • In‑Browser Documentation: Full API reference, guides, and source‑code walkthroughs rendered from Markdown with syntax highlighting, copy buttons, and a navigable sidebar

To run the playground:

cd playground
yarn install
yarn dev

The playground serves as both a learning resource and a testing ground for new features, showcasing best practices and real-world usage patterns.

API Reference

Core Primitives

createSignal<T>(initialValue: T, options?: ReactiveOptions): [SignalGetter<T>, SignalSetter<T>]

Creates a reactive state container. Returns a tuple containing a getter and a setter.

createEffect(fn: () => void, options?: ReactiveOptions): Disposer

Creates a reactive scope that automatically re-runs when its dependencies change.

createMemo<T>(fn: () => T, options?: ReactiveOptions): Memo<T>

Creates a derived, read-only signal that caches its value.

untrack<T>(fn: () => T): T

New in v1.1.0 - Executes a function without tracking its dependencies, preventing the current reactive scope from re-running when signals inside the function change.

Useful for:

  • Creating component instances that maintain independent state
  • Performing side effects without triggering reactive updates
  • Breaking unwanted dependency chains
const [count, setCount] = createSignal(0);

// This effect normally re-runs when count changes
createEffect(() => {
  console.log("Count:", count());
});

// This won't trigger the effect
untrack(() => {
  console.log("Silent read:", count()); // Not tracked
});

createResource<S, T>(source: SignalGetter<S>, fetcher: Fetcher<S, T>): Resource<T>

Creates a signal for managing asynchronous data, complete with reactive .loading and .error states.

New in v1.3.0: The fetcher now receives a second argument info: { signal: AbortSignal } – an AbortSignal that aborts when the resource is disposed or the source changes. This allows you to cancel in‑flight requests cleanly.

const [userId] = createSignal(1);
const user = createResource(userId, async (id, { signal }) => {
  const res = await fetch(`/api/users/${id}`, { signal });
  return res.json();
});

longestIncreasingSubsequenceAsync(array: Int32Array): Promise<number[]>

High-performance async implementation of the longest increasing subsequence algorithm for large arrays, using Web Workers for non-blocking computation.

configureLIS(options: { smallArrayThreshold: number }): void

Configures the threshold for switching between fast and optimized LIS algorithms.

AidedError

Custom error class for better debugging of reactive issues.

Lifecycle & Context

createRoot(fn: (dispose: Disposer) => void): Disposer

Creates a top-level ownership scope for automatic memory management.

onCleanup(fn: Disposer): void

Registers a cleanup function to run when the current reactive scope is disposed. Essential for cleaning up event listeners, timers, and other resources.

createEffect(() => {
  const timer = setInterval(() => console.log("tick"), 1000);

  onCleanup(() => {
    clearInterval(timer); // Clean up when effect re-runs or scope ends
  });
});

createContext<T>(defaultValue?: T): Context<T>

Creates a context object for providing data throughout a component tree.

provide<T>(context: Context<T>, value: T): void

Provides a value for a context within the current scope.

useContext<T>(context: Context<T>): T | undefined

Consumes a value from the nearest context provider.

Rendering & DOM

render(component: () => Element, mountNode: Element): Disposer

The main entry point for an application. It mounts a component into a DOM node within a new reactive root.

h (Hyperscript)

The h helper is the primary way to build UI in Aided. It's a proxy that provides a function for every HTML tag (e.g., h.div, h.a).

import { h, createSignal } from "aided-core";

const [name, setName] = createSignal("World");
const [isActive, setIsActive] = createSignal(true);

const element = h.div(
  // Attributes and event handlers go in an object
  {
    id: "container",
    classList: { active: isActive, static: true },
    style: { color: () => (isActive() ? "blue" : "grey") },
    onClick: () => console.log("Clicked!"),
  },
  // Children follow the attributes
  "Hello, ",
  name,
);
  • Reactive Children: Passing a signal (name) as a child automatically creates a reactive text node.
  • Reactive Attributes: Passing a signal as an attribute value (id: myId) creates a reactive binding.
  • Special Properties: h has special handling for classList, style, ref, and event handlers (onClick, onInput, etc.).

Model (Two-Way Binding)

The Model helper provides two-way binding for form inputs. It's used with the ref property in the h helper.

const nameSignal = createSignal("");
const input = h.input({
  ref: (el) => Model(el, nameSignal),
});

Structural Components

Show(props: { when, children, fallback? }): Node

Conditionally renders children if when() is truthy, otherwise renders fallback.

Show({
  when: isLoggedIn,
  fallback: () => h.p("Please log in."),
  children: () => h.p("Welcome!"),
});

For(props: { each, key?, children }): Node

Efficiently renders a list of items using a keyed reconciliation algorithm.

const [items] = createSignal(["a", "b"]);
const list = h.ul(
  For({
    each: items,
    key: (item) => item,
    children: (item) => h.li(item),
  }),
);

createVirtualizer<T>(options): Virtualizer<T>

Creates a headless, high-performance engine for virtualizing large lists. It contains all the state and logic for calculating the visible window of items, which can then be used by a rendering component.

  • options.items: A signal containing the full list of data.
  • options.itemHeight: The fixed height of each item in pixels.
  • options.overscan: The number of extra items to render on either side of the visible area.

Returns a Virtualizer object with reactive properties:

  • .visibleItems: A memoized array of the items that should be rendered.
  • .totalHeight: A memoized total height of the scrollable area.
  • .visibleState: A memoized object containing { startIndex, endIndex, scrollOffset }.
  • .setContainer: A function to pass the scrollable container element to the virtualizer.

VirtualFor<T>(props): HTMLElement

An efficient, high-performance component for rendering virtualized lists. It renders only the items currently visible in the scrollable area, making it suitable for lists with thousands or millions of rows.

const [items] = createSignal(
  Array.from({ length: 100000 }, (_, i) => `Item ${i}`),
);

// Simple usage
const list = VirtualFor({
  each: items,
  itemHeight: 30, // Each row is 30px high
  overscan: 5, // Render 5 extra items above/below the viewport
  children: (item, index) => h.div({ class: "row" }, `${index}: ${item}`),
});

// With container customization
const customList = VirtualFor({
  each: items,
  itemHeight: 30,
  overscan: 5,
  containerProps: {
    className: "my-scroller",
    style: { height: "400px", border: "1px solid #ccc" },
    attributes: [
      { name: "data-testid", value: "virtual-list" },
      { name: "aria-label", value: "Virtualized item list" },
    ],
  },
  children: (item, index) => h.div({ class: "row" }, `${index}: ${item}`),
});

Props:

  • each: A signal containing the array of items.
  • itemHeight: The fixed height of each item in pixels.
  • children: A function that receives the item and its index and returns a DOM node.
  • overscan?: The number of extra items to render on either side (default: 5).
  • containerProps?: Optional configuration for the scroll container:
    • className?: CSS class name(s) for the container
    • style?: Inline styles for the container
    • attributes?: Array of custom attributes (e.g., [{ name: 'data-testid', value: 'list' }])
  • placeholder?: An optional element to show when the each array is empty.

Note: The attributes array accepts Attribute objects with name and value properties. Dangerous attributes like ref, role, style, class, and className are automatically filtered for safety.

Fragment(props: { children: Node[] }): DocumentFragment

Groups multiple children without adding a wrapper element to the DOM.

Portal(props: { mount: Element, children: Node }): Comment

Renders children into a different DOM mount node.

Performance & Trade-offs

Aided achieves exceptional performance through:

  • Direct DOM Manipulation: No Virtual DOM diffing - effects bind directly to DOM nodes
  • Fine-Grained Updates: Only code dependent on changed state re-executes
  • Surgical Reconciliation: The For component uses an optimized LIS algorithm for minimal DOM operations
  • Virtual Scrolling: VirtualFor renders only visible items for millions of rows

Bundle Size: 5.92kb minified + gzipped Memory: Automatic cleanup prevents leaks through ownership graph Runtime: Zero dependencies, pure JavaScript execution

The trade-off is that it does not use JSX, instead opting for a hyperscript function (h) for UI creation. The keyed reconciliation in For is highly efficient for lists with stable keys. For extremely large datasets, the VirtualFor component provides best-in-class rendering performance.

Security Strengthening (v1.2.0)

Version 1.2.0 introduced proactive security measures to prevent common injection attacks.

bindAttr – Event Handler Rejection

The bindAttr utility now rejects any attribute name starting with on (case‑insensitive), such as onclick, onload, onerror. Trying to bind such attributes throws a descriptive error:

// ❌ This now throws an error
bindAttr(button, "onclick", () => console.log("clicked"));
// Error: Security: Cannot bind event handler attribute 'onclick'. Use addEventListener() instead.

Always use addEventListener or the onClick prop in h() for event handling.

h Proxy – Tag Name Validation

The h hyperscript helper now validates all tag names and blocks dangerous ones:

  • Blocked tags: script, constructor, prototype (throws a Security error).
  • Tag name format: Must start with a letter and contain only letters, numbers, and hyphens: /^[a-zA-Z][a-zA-Z0-9-]*$/.
// ❌ These now throw errors
h.script(); // Security: Cannot create 'script' element...
h["1div"](); // Invalid tag name '1div'...
h["constructor"](); // Security: Cannot create 'constructor' element...

// ✅ Still allowed
h.div();
h["my-custom-element"]();

For Component – Null Children Support

The children function of the For component can now return null (or undefined) to skip rendering an item entirely. The component properly disposes the reactive root for that item, preventing memory leaks.

For({
  each: items,
  children: (item) => {
    if (item().hidden) return null; // skip this item
    return h.div({}, item().text);
  },
});

This is especially useful for conditional rendering inside lists.

What’s New in v1.3.0

Performance Profiler (Zero‑Overhead)

A new built‑in profiler lets you track effect execution times without affecting production performance. When disabled, it adds only a single boolean check.

import { enableProfiler, getProfilerReport } from "aided-core";

enableProfiler(true);

// ... run your app ...

const report = getProfilerReport();
console.log(report.effectExecutions); // total number of effect runs
console.log(report.effects); // per‑effect counts and total time

createResource with AbortSignal

The fetcher now receives an AbortSignal as part of the second argument, allowing you to cancel in‑flight requests when the resource is re‑fetched or the owning scope is disposed.

const user = createResource(
  () => userId(),
  async (id, { signal }) => {
    const res = await fetch(`/api/users/${id}`, { signal });
    return res.json();
  },
);

Stronger XSS Prevention – URL Protocol Blocking

bindAttr now blocks dangerous URL protocols (javascript:, vbscript:, data:) on URL‑sensitive attributes (href, src, action, formaction, xlink:href, srcdoc, poster). Both static and reactive values are validated, and a clear Security error is thrown.

h.dangerous – Explicit Escape Hatch

The h proxy now provides a .dangerous namespace for creating tags that are blocked by default (e.g., script, iframe, base, meta, link, object, embed). This forces developers to explicitly opt in and acknowledges the security risk; a warning is logged in development.

// ❌ Blocked by default
h.script(); // throws

// ✅ Explicit opt‑in
h.dangerous.script(); // works (with dev warning)

The namespace still hard‑blocks prototype escapes (constructor, prototype, __proto__).

Scheduler Error Isolation

Errors thrown inside effects are now caught and logged individually. One failing effect no longer prevents other dirty effects from executing during the same flush batch. The log includes the effect name for easier debugging.

Portal DocumentFragment Fix

Portal now correctly handles DocumentFragment children, tracking individual child nodes and preventing NotFoundError crashes during cleanup.

Testing

Aided includes comprehensive test coverage to ensure reliability and correctness:

Unit Tests

The core library has extensive unit tests covering all reactive primitives, components, and utilities. Tests are written using Vitest and achieve high code coverage.

yarn test

E2E Tests

End-to-end tests validate the complete user experience in real browsers using TestCafe. These tests run against the interactive playground and verify:

  • Navigation and routing
  • Interactive examples functionality
  • Reactive state updates
  • Cross-browser compatibility (Chrome, Firefox)
# Run all E2E tests
yarn test:e2e

# Run in specific browser
yarn test:e2e:chrome
yarn test:e2e:firefox

# Run in headed mode (see browser)
yarn test:e2e:headed

E2E tests use the Page Object Model pattern for maintainability and include comprehensive coverage of all playground examples. See e2e/README.md for detailed documentation.

Contributing

Contributions are welcome! Please open an issue to discuss your ideas before submitting a pull request. See the CONTRIBUTING.md for more details on how to get started.

Acknowledgements

The architecture and API of Aided are heavily inspired by the excellent work of SolidJS and its fine-grained reactivity model.

License

MIT