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

aix

v0.8.0

Published

aix is a react native package built with Nitro

Readme

AIX

UI Primitives for building AI apps in React Native.

[!WARNING] aix is currently in alpha preview. The API is likely to change.

Features

  • Start a chat scrolled to end on the first frame
  • Animate scrolling to new messages when they send
  • Float messages to the top of the screen with automated "blank size" handling
  • Animate message content as it streams
  • Keyboard handling out-of-the-box with no external dependencies
  • Support for absolute-positioned composers
  • Detect "is scrolled near end" for ScrollToEnd buttons

To learn about the motivation behind AIX, you can read our blog post on How we built the v0 iOS app. AIX is an opinionated, feature-complete, and extensible way to implement every single feature mentioned in that blog post.

When building AIX, we started by copying the code from v0 into a separate repository. However, as we worked to make it flexible for use cases outside of our own app, we decided to rewrite it from scratch in native code. What you see here is a Nitro Module which handles all business logic in UIKit. We plan on adding support for Android as well and welcome contributions.

Installation

npm i aix react-native-nitro-modules

Next, rebuild your native app. For Expo users, run npx expo prebuild and rebuild.

Usage

Wrap your ScrollView with Aix, and wrap your messages with AixCell.

import { Aix, AixCell } from 'aix'
import { Message } from 'path/to/your/message'
import { Composer } from 'path/to/your/composer'

export function ChatScreen({ messages }) {
  return (
    <Aix style={{ flex: 1 }} shouldStartAtEnd>
      <ScrollView>
        {messages.map((message) => (
          <AixCell
            key={message.id}
            index={index}
            isLast={index === messages.length - 1}
          >
            <Message message={message} />
          </AixCell>
        ))}
      </ScrollView>
    </Aix>
  )
}

Add a floating composer

To add a floating composer which lets content scroll under it, you can use the AixFooter. Pair it with Aix.scrollOnFooterSizeUpdate to ensure content scrolls under the footer when it changes size.

import { Aix, AixCell, AixFooter } from 'aix'

export function ChatScreen({ messages }) {
  const { bottom } = useSafeAreaInsets()

  return (
    <Aix
      style={{ flex: 1 }}
      shouldStartAtEnd
      scrollOnFooterSizeUpdate={{
        enabled: true,
        scrolledToEndThreshold: 100,
        animated: false,
      }}
    >
      <ScrollView>
        {messages.map((message) => (
          <AixCell
            key={message.id}
            index={index}
            isLast={index === messages.length - 1}
          >
            <Message message={message} />
          </AixCell>
        ))}
      </ScrollView>

      <AixFooter
        style={{ position: 'absolute', inset: 0, top: 'auto' }}
        stickToKeyboard={{
          enabled: true,
          offset: {
            whenKeyboardOpen: 0,
            whenKeyboardClosed: -bottom,
          },
        }}
      >
        <Composer />
      </AixFooter>
    </Aix>
  )
}

Send a message

When sending a message, you will likely want to scroll to it after it gets added to the list. Use the scrollToIndex prop to declaratively trigger an animated scroll, and onDidScrollToIndex to reset the state when the scroll completes.

The index you pass should correspond to the newest message in the list. For AI chats, this is typically the next assistant message index. Use a nullish value (null | undefined) to indicate no scroll target.

import { Keyboard } from 'react-native'
import { useState } from 'react'

function Chat() {
  const [scrollToIndex, setScrollToIndex] = useState<number | null>(null)

  const onSubmit = () => {
    const newMessageIndex = messages.length + 1
    setScrollToIndex(newMessageIndex)
    sendMessage(message)
    requestAnimationFrame(Keyboard.dismiss)
  }

  return (
    <Aix
      scrollToIndex={scrollToIndex}
      onDidScrollToIndex={() => setScrollToIndex(null)}
    >
      {/* ... */}
    </Aix>
  )
}

Add a scroll to end button

You can use onScrolledNearEndChange to show a "scroll to end" button when the user scrolls away from the bottom:

import { Aix, useAixRef } from 'aix'
import { useState } from 'react'
import { Button } from 'react-native'

function Chat() {
  const aix = useAixRef()
  const [isNearEnd, setIsNearEnd] = useState(false)

  return (
    <Aix
      ref={aix}
      scrollEndReachedThreshold={200}
      onScrolledNearEndChange={setIsNearEnd}
    >
      {/* ScrollView and messages... */}

      {!isNearEnd && (
        <Button
          onPress={() => aix.current?.scrollToEnd(true)}
          title='Scroll to end'
        />
      )}
    </Aix>
  )
}

API Reference

Aix

The main container component that provides keyboard-aware behavior and manages scrolling for chat interfaces.

Props

| Prop | Type | Default | Description | | --------------------------------- | ------------------------------ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | shouldStartAtEnd | boolean | - | Whether the scroll view should start scrolled to the end of the content. | | scrollOnFooterSizeUpdate | object | { enabled: true, scrolledToEndThreshold: 100, animated: false } | Control the behavior of scrolling when the footer size changes. By default, changing the height of the footer will shift content up in the scroll view. | | scrollEndReachedThreshold | number | max(blankSize, 200) | The number of pixels from the bottom of the scroll view to the end of the content that is considered "near the end". Used by onScrolledNearEndChange and to determine if content should shift up when keyboard opens. | | onScrolledNearEndChange | (isNearEnd: boolean) => void | - | Callback fired when the scroll position transitions between "near end" and "not near end" states. Reactive to user scrolling, content size changes, parent size changes, and keyboard height changes. Uses scrollEndReachedThreshold to determine the threshold. | | additionalContentInsets | object | - | Additional content insets applied when keyboard is open or closed. Shape: { top?: { whenKeyboardOpen, whenKeyboardClosed }, bottom?: { whenKeyboardOpen, whenKeyboardClosed } } | | additionalScrollIndicatorInsets | object | - | Additional insets for the scroll indicator, added to existing safe area insets. Applied to verticalScrollIndicatorInsets on iOS. | | mainScrollViewID | string | - | The nativeID of the scroll view to use. If provided, will search for a scroll view with this accessibilityIdentifier. | | penultimateCellIndex | number | - | The index of the second-to-last message (typically the last user message in AI chat apps). Used to determine which message will be scrolled into view. Useful when you have custom message types like timestamps in your list. | | scrollToIndex | number | - | When set, triggers an animated scroll to this cell index. Use a nullish value (null or undefined) to indicate no scroll target. After the scroll completes, onDidScrollToIndex is called. Reset after receiving the callback. | | onDidScrollToIndex | () => void | - | Called when the animated scroll to scrollToIndex completes. Use this to clear the scrollToIndex state. |

Ref Methods

Access these methods via useAixRef():

const aix = useAixRef()

// Scroll to the end of the content
aix.current?.scrollToEnd(animated)

| Method | Parameters | Description | | ------------- | -------------------- | ---------------------------------- | | scrollToEnd | animated?: boolean | Scrolls to the end of the content. |

Note: For scrolling to a specific message index, prefer using the declarative scrollToIndex prop instead of imperative methods.


AixCell

A wrapper component for each message in the list. It communicates cell position and dimensions to the parent Aix component.

Props

| Prop | Type | Required | Description | | -------- | --------- | -------- | ------------------------------------------------------------------------------------------- | | index | number | Yes | The index of this cell in the message list. | | isLast | boolean | Yes | Whether this cell is the last item in the list. Used for scroll positioning and animations. |


AixFooter

A footer component for floating composers that allows content to scroll underneath it. The footer's height is automatically tracked for proper scroll offset calculations.

Props

Accepts all standard React Native View props.

Important Notes

  • Do not apply vertical padding (padding, paddingBottom) directly to AixFooter. Apply padding to a child view instead.
  • Position the footer absolutely at the bottom of the Aix container:
<AixFooter style={{ position: 'absolute', inset: 0, top: 'auto' }}>
  <Composer />
</AixFooter>

Rules

  • Do NOT apply padding to contentContainerStyle. Instead, use padding on children directly.
  • Do NOT apply padding to AixFooter. Instead, use padding on its children

TODOs

  • [ ] Android support
  • [ ] LegendList support
  • [ ] FlashList support

Requirements

  • React Native v0.78.0 or higher
  • Node 18.0.0 or higher