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

airx

v0.7.10

Published

a front framework with jsx

Readme

Airx ☁️

npm build status TypeScript MIT License

A lightweight, Signal-driven JSX web application framework

中文文档English Documentation

Airx is a modern frontend framework built on JSX and Signal primitives, designed to provide a simple, performant, and intuitive solution for building reactive web applications.

✨ Features

  • 🔄 Signal-driven reactivity: Seamlessly integrates with TC39 Signal proposal
  • 📝 TypeScript-first: Developed entirely in TypeScript with excellent type safety
  • Functional components: Define components using clean JSX functional syntax
  • 🚫 No hooks complexity: Simple and straightforward API without React-style hooks
  • 🪶 Lightweight: Minimal bundle size with zero dependencies
  • 🔌 Extensible: Plugin system for advanced functionality
  • 🌐 Universal: Works in both browser and server environments

🚀 Quick Start

Installation

npm install airx
# or
yarn add airx
# or
pnpm add airx

Basic Usage

import * as airx from 'airx'

// Create reactive state using Signal
const count = new Signal.State(0)
const doubleCount = new Signal.Computed(() => count.get() * 2)

function Counter() {
  const localState = new Signal.State(0)

  const increment = () => {
    count.set(count.get() + 1)
    localState.set(localState.get() + 1)
  }

  // Return a render function
  return () => (
    <div>
      <h1>Counter App</h1>
      <p>Global count: {count.get()}</p>
      <p>Double count: {doubleCount.get()}</p>
      <p>Local count: {localState.get()}</p>
      <button onClick={increment}>
        Click me!
      </button>
    </div>
  )
}

// Create and mount the app
const app = airx.createApp(<Counter />)
app.mount(document.getElementById('app'))

🌐 Server-Side Rendering (SSR)

Airx supports server-side rendering (SSR) out of the box. SSR allows you to render your components to HTML strings on the server, which can improve initial page load performance and SEO.

Quick Start

import * as airx from 'airx'

// Create an SSR app
const app = airx.createSSRApp(<MyComponent />)

// Render to HTML string
const html = await app.renderToString()
// html === '<div><h1>Hello World</h1></div>'

Full SSR Example

import { createSSRApp } from 'airx'

// Define a component
function UserCard({ name, email }: { name: string; email: string }) {
  return () => (
    <div className="user-card">
      <h2>{name}</h2>
      <p>{email}</p>
    </div>
  )
}

// Server-side rendering
async function renderPage() {
  const app = createSSRApp(
    <UserCard name="Alice" email="[email protected]" />
  )
  
  const html = await app.renderToString()
  console.log(html)
  // <div class="user-card"><h2>Alice</h2><p>[email protected]</p></div>
  
  return html
}

Hydration (Client-Side Activation)

⚠️ Note: Hydration support is planned for 0.8.x release. Currently, hydrate() is available as a stub for future implementation.

// Future: Activate SSR HTML on the client
import { createSSRApp, hydrate } from 'airx'

async function hydrateApp(ssrHtml: string) {
  const app = createSSRApp(<App />)
  
  const container = document.getElementById('app')
  if (container) {
    hydrate(ssrHtml, container, app)
  }
}

API Reference

createSSRApp(element)

Creates an SSR application instance for server-side rendering.

const app = airx.createSSRApp(<MyComponent />)

renderToString(app)

Renders an SSR app to an HTML string (returns a Promise).

const html = await airx.renderToString(app)

hydrate(html, container, app)

Activates server-rendered HTML on the client (planned for 0.8.x).

📖 Core Concepts

Components

Components in Airx are simple functions that return a render function:

function MyComponent() {
  const state = new Signal.State('Hello')
  
  return () => (
    <div>{state.get()} World!</div>
  )
}

State Management

Airx leverages the Signal primitive for reactive state management:

// State
const count = new Signal.State(0)

// Computed values
const isEven = new Signal.Computed(() => count.get() % 2 === 0)

// Effects
const effect = new Signal.Effect(() => {
  console.log('Count changed:', count.get())
})

Context & Dependency Injection

const ThemeContext = Symbol('theme')

function App() {
  // Provide values down the component tree
  airx.provide(ThemeContext, 'dark')
  
  return () => <Child />
}

function Child() {
  // Inject values from parent components
  const theme = airx.inject(ThemeContext)
  
  return () => (
    <div className={`theme-${theme}`}>
      Current theme: {theme}
    </div>
  )
}

Lifecycle Hooks

function Component() {
  airx.onMounted(() => {
    console.log('Component mounted')
    
    // Return cleanup function
    return () => {
      console.log('Component unmounted')
    }
  })

  airx.onUnmounted(() => {
    console.log('Component will unmount')
  })
  
  return () => <div>My Component</div>
}

## 📚 API Reference

Airx follows a minimal API design philosophy. Here are the core APIs:

### `createApp(element)`

Creates an application instance.

```tsx
const app = airx.createApp(<App />)
app.mount(document.getElementById('root'))

provide<T>(key, value): ProvideUpdater<T>

Provides a value down the component tree through context. Must be called synchronously within a component.

function Parent() {
  airx.provide('theme', 'dark')
  return () => <Child />
}

inject<T>(key): T | undefined

Retrieves a provided value from the component tree. Must be called synchronously within a component.

function Child() {
  const theme = airx.inject('theme')
  return () => <div>Theme: {theme}</div>
}

onMounted(listener): void

Registers a callback for when the component is mounted to the DOM.

type MountedListener = () => (() => void) | void

airx.onMounted(() => {
  console.log('Mounted!')
  return () => console.log('Cleanup')
})

onUnmounted(listener): void

Registers a callback for when the component is unmounted from the DOM.

type UnmountedListener = () => void

airx.onUnmounted(() => {
  console.log('Unmounted!')
})

createElement(type, props, ...children)

Creates virtual DOM elements (usually handled by JSX transpiler).

Fragment

A component for grouping multiple elements without adding extra DOM nodes.

function App() {
  return () => (
    <airx.Fragment>
      <div>First</div>
      <div>Second</div>
    </airx.Fragment>
  )
}

🔧 Development

Building from Source

# Clone the repository
git clone https://github.com/airxjs/airx.git
cd airx

# Install dependencies
npm install

# Build the project
npm run build

# Run tests
npm test

# Run tests with UI
npm run test:ui

# Run tests with coverage
npm run test:coverage

Project Structure

source/
├── app/           # Application creation and management
├── element/       # Virtual DOM and JSX handling
├── logger/        # Internal logging utilities
├── render/        # Rendering engine
│   ├── basic/     # Core rendering logic
│   ├── browser/   # Browser-specific rendering
│   └── server/    # Server-side rendering
├── signal/        # Signal integration
├── symbol/        # Internal symbols
└── types/         # TypeScript type definitions

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Workflow

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests for your changes
  5. Ensure all tests pass (npm test)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • Thanks to all contributors and supporters of the Airx project
  • Inspired by the TC39 Signal proposal
  • Built with ❤️ by the Airx community

📞 Support


Made with ☁️ by the Airx team