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

xote

v6.4.0

Published

![NPM Version](https://img.shields.io/npm/v/xote) ![npm bundle size](https://badgen.net/bundlephobia/min/xote) ![npm bundle size](https://badgen.net/bundlephobia/minzip/xote)

Readme

xote

NPM Version npm bundle size npm bundle size

xote is a lightweight ReScript library that combines fine-grained reactivity and a declarative component system for building user interfaces for the web.

Getting Started

Installation

npm install xote

Then, add it to your ReScript project's rescript.json. You'll need to declare xote as a dependency and configure JSX to use Xote's transform:

{
  "dependencies": ["xote"],
  "jsx": {
    "version": 4,
    "module": "XoteJSX"
  },
  "compiler-flags": ["-open Xote"]
}

The compiler flag -open Xote is optional, it makes the Xote modules available unqualified inside your source files.

This README uses the application-facing names introduced for public code:

  • View is the official module for building and mounting DOM nodes.
  • Node is a deprecated compatibility alias for View and will be removed in a future release.
  • Prop is the official static-or-reactive prop module.
  • ReactiveProp is a deprecated compatibility alias for Prop.
  • View.signalText, View.each, View.eachWithKey, View.Attr.*, Router.location, and SSRState.signal are preferred in examples, while the deprecated Node.signalText, Node.list, Node.keyedList, Node.attr, ReactiveProp.*, and SSRState.make names remain supported.

Quick Example

module App = {
  @jsx.component
  let make = () => {
    // Create reactive state
    let count = Signal.make(0)

    // Create a derived state
    let doubled = Computed.make(() => Signal.get(count) * 2)

    // Logs every time count changes:
    Effect.run(() => {
      Console.log2("Count is ", Signal.get(count))
      
      None // Optional clean up function
    })

    // Build the UI with JSX
    <div>
      <h1> {View.text("Counter")} </h1>
      <p>
        {View.signalText(() => "Count: " ++ Signal.get(count)->Int.toString)}
      </p>
      <p>
        {View.signalText(() => "Doubled: " ++ Signal.get(doubled)->Int.toString)}
      </p>
      <button onClick={(_evt: Dom.event) => Signal.update(count, n => n + 1)}>
        {View.text("Increment")}
      </button>
    </div>
  }
}

// Mount to the DOM
View.mountById(<App />, "app")

Since in ReScript each file is its own module, you can define a reusable component by exporting a make function from that file. The file name becomes the component name: Counter.res gives you <Counter />.

The @jsx.component attribute instructs the compiler to derive a props type from the function's labeled arguments, enabling JSX usage without boilerplate.

Here's an example of a reusable component with properties:

// Greeting.res
@jsx.component
let make = (~name: string, ~greeting: string="Hello") => {
  <p>
    {View.text(greeting ++ ", " ++ name ++ "!")}
  </p>
}

// Usage from another file:
<Greeting name="World" /> // <p>Hello, World!</p>
<Greeting name="Universe" greeting="Hey" /> // <p>Hey, Universe!</p>

Core Concepts

Xote focuses on clarity, control, and performance. The goal is to offer precise, fine-grained updates and predictable behavior with a minimal set of abstractions, while leveraging the robust type system from ReScript.

Reactive Primitives

Xote uses rescript-signals for its reactive primitives:

  • Signal: Reactive state container - Signal.make(value)
  • Computed: Derived reactive value that updates automatically - Computed.make(() => ...)
  • Effect: Side-effect functions that re-run when dependencies change - Effect.run(() => ...)

All reactive primitives feature automatic dependency tracking. No manual subscriptions needed.

View System

On top of the reactive primitives with signals, Xote provides a declarative view system:

  • JSX Support: Build user interface using JSX in a declarative and familiar manner
  • Reactive DOM Nodes: Fine-grained reactivity that updates DOM nodes directly, no virtual DOM required
  • Built-in Router: Client-side routing with pattern matching and a reactive location state
  • Automatic Cleanup: Effect disposal and memory management built into the component lifecycle
  • Server-side Rendering: pre-render your pages on the server with full hydration (experimental)

Views and Attributes

View creates UI nodes. It is the official application-facing module for DOM rendering:

let className = Signal.make("card")

Html.div(
  ~attrs=[View.Attr.signal("class", className)],
  ~children=[
    View.text("Status: "),
    View.signalText(() => Signal.get(className)),
  ],
  (),
)

For rendering collections, prefer View.each for simple lists and View.eachWithKey when items have stable identity:

type todo = {id: string, title: string}

let todos = Signal.make([
  {id: "1", title: "Write docs"},
  {id: "2", title: "Ship release"},
])

View.eachWithKey(
  todos,
  todo => todo.id,
  todo => <li> {View.text(todo.title)} </li>,
)

Static or Reactive Props

Use Prop when a component prop can accept either a static value or a signal:

@jsx.component
let make = (~className: Prop.t<string>=Prop.static("badge"), ~children) => {
  <span class={className}> {children} </span>
}

let tone = Signal.make("badge badge-info")

<Badge className={Prop.signal(tone)}>
  {View.text("Live")}
</Badge>

Prop is the source module for static-or-reactive props. ReactiveProp remains available as a deprecated compatibility alias.

Router and SSR State

Router state is signal-based. Read the shared location signal directly with Router.location():

Router.init(())

let pathname = View.signalText(() => {
  let location = Signal.get(Router.location())
  "Current path: " ++ location.pathname
})

For server/client state transfer, prefer SSRState.signal when creating a synced signal:

let count = SSRState.signal("count", 0, SSRState.Codec.int)

Check the website for more comprehensive documentations about Xote and Signals.

License

LGPL v3