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

@intentface/chat

v0.1.1

Published

Headless chat UI primitives — unstyled compound components, hooks, and wire formats for building AI chat interfaces.

Downloads

238

Readme

@intentface/chat

Headless chat UI primitives for React — the behavior, state, and wire formats for building AI chat interfaces, with no styling of their own.

This is the Base UI model applied to chat: the package owns behavior, you own every class. There is no pre-styled @intentface/chat package. The demos on each docs page show how the parts fit together and are meant to be copied and restyled.

Status

Pre-1.0. The primitives are in real use, but the API is still settling and minor releases may contain breaking changes — the usual 0.x semver contract. Pin an exact version if that matters to you, and check the changelog when upgrading.

Install

bun add @intentface/chat

react and react-dom (v19+) are peer dependencies. Nothing else ships with it beyond two small runtime deps (@floating-ui/dom for anchored positioning, nanoid for attachment ids) — no editor framework, no animation library.

What's inside

Each primitive is a separate entry point, exporting an unstyled compound component that renders semantic DOM with data-* state attributes, context hooks, and a render prop for swapping the underlying element:

| Import | What it is | | --- | --- | | @intentface/chat/composer | Rich-text input over a purpose-built contenteditable engine: / and @ command palette, inline chips, attachments, ask-user flow. One store per <Composer>, or bring your own via Composer.createStore(). | | @intentface/chat/thread | Scroll container with at-bottom detection and auto-follow. | | @intentface/chat/message | Message parts, turns, chip-segmented text, sources, actions. | | @intentface/chat/steps, /reasoning | Tool-call timelines and reasoning disclosure. | | @intentface/chat/chip, /attachments, /ask-user | The remaining building blocks. | | @intentface/chat/types | The structural message contract + part type guards. | | @intentface/chat/message-utils | Part segmentation, turn grouping, reasoning/source derivation. | | @intentface/chat/chip-markdown | The self-describing chip wire format. |

AI SDK, structurally

The types are framework-agnostic. A Vercel AI SDK UIMessage satisfies ChatMessage structurally, so there's no adapter — pass SDK messages straight into the utilities and components:

import { groupTurns } from "@intentface/chat/message-utils";
import type { UIMessage } from "ai";

const turns = groupTurns(messages); // messages: UIMessage[]

React Server Components

Each primitive is a namespace, and the root is explicit — Message.Root, not Message. Server components can reach every part:

// A server component — no "use client" needed.
import { Message } from "@intentface/chat/message";

export const Transcript = ({ messages }: { messages: ChatMessage[] }) => (
  <>
    {messages.map((message) => (
      <Message.Root key={message.id} role={message.role}>
        <Message.Text>{text(message)}</Message.Text>
      </Message.Root>
    ))}
  </>
);

The parts are still client components — they carry their own "use client" — so interactive props behave the way React requires. Pass an event handler from a server component and you'll get React's usual error; move that piece into a client component:

"use client";
import { Composer } from "@intentface/chat/composer";

export const Chat = () => (
  <Composer.Root onSubmit={(data) => send(data)}>
    <Composer.Container>
      <Composer.Textarea />
    </Composer.Container>
  </Composer.Root>
);

Hooks (useComposer, useThread, useReasoning, …) are client-only, as usual. The pure modules — /types, /message-utils, /chip-markdown — carry no directive and import fine anywhere.

Docs

Full guides, live demos, and the API reference at intentface.dev/docs.

License

MIT