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

@assistant-ui/react-generative-ui

v0.0.21

Published

Generative UI components for assistant-ui

Readme

@assistant-ui/react-generative-ui

Generative UI tools for assistant-ui.

Installation

npm install @assistant-ui/react-generative-ui

Usage

Declare the components the model is allowed to render in a library, then expose them through the present tool. The model emits a { $type, ...props } tree ($type names the component, so a real type prop never collides); the tool renders it against the library.

import { JSONGenerativeUI } from "@assistant-ui/react-generative-ui";
import { Thread, Tools } from "@assistant-ui/react";
import { z } from "zod";

const generativeUI = new JSONGenerativeUI({
  library: {
    Card: {
      description: "A card container with a title.",
      properties: z.object({ title: z.string() }),
      render: ({ title, children }) => (
        <section className="card">
          <h3>{title}</h3>
          {children}
        </section>
      ),
    },
    Text: {
      description: "A run of text.",
      properties: z.object({ tone: z.enum(["muted", "normal"]).optional() }),
      render: ({ tone, children }) => <p data-tone={tone}>{children}</p>,
    },
  },
});

const toolkit = {
  present: generativeUI.present(),
};

function App() {
  return (
    <Thread>
      <Tools toolkit={toolkit} />
      {/* MessageList, Composer, ... */}
    </Thread>
  );
}

A node's children prop is rendered recursively, so components can be nested:

{
  "$type": "Card",
  "title": "Hello",
  "children": [{ "$type": "Text", "tone": "muted", "children": "Nested text" }]
}

Streaming props

By default a component renders only once its props have fully arrived, so render always sees complete props. Opt into rendering from partial props with streamProperties. When you do, render receives an injected $status prop that discriminates the props: while "streaming" they are Partial, and once "done" they are complete.

const generativeUI = new JSONGenerativeUI({
  library: {
    Weather: {
      description: "A live weather card.",
      properties: z.object({ city: z.string(), temp: z.number() }),
      streamProperties: true,
      render: (props) => {
        if (props.$status === "done") {
          return <Card>{`${props.city}: ${props.temp}°`}</Card>; // complete props
        }
        return <Card>{props.city ?? "Loading…"}</Card>; // partial props
      },
    },
  },
});

render is a function of props, not a React component — but it runs on its own fiber, so you can call hooks inside it as usual.

"use generative" authoring

The examples above wire the library up by hand on the client. With the "use generative" compiler (@assistant-ui/next or @assistant-ui/vite) you can instead colocate a component's properties schema with its render and expose the library as tools, and the build splits each half to the right target: the schema goes to the server (so the model sees the tool), the render stays on the client.

"use generative";

import { z } from "zod";
import { Weather } from "@/components/weather";
import { defineToolkit } from "@assistant-ui/react";
import {
  JSONGenerativeUI,
  defineGenerativeComponents,
} from "@assistant-ui/react-generative-ui";

const generative = new JSONGenerativeUI({
  library: defineGenerativeComponents({
    Weather: {
      description: "Show a weather card.",
      properties: z.object({ city: z.string() }),
      render: (props) => <Weather {...props} />,
    },
  }),
});

export default defineToolkit({
  // `present` displays a component; `prompt_user` is a human-in-the-loop prompt.
  present: generative.present(),
  prompt_user: generative.promptUser(),
});

The model calls present with a node like { "$type": "Weather", "city": "SF" }. Pass present({ display: "standalone" }) to render the component on its own surface instead of inline. See the "use generative" docs for the build setup.

Slack, Teams, and A2UI

The same tree is plain JSON, so it is not tied to the browser. Three React-free subpaths convert it for Slack Block Kit, Microsoft Teams Adaptive Cards, and inbound A2UI surfaces. They run in server actions, queue workers, and webhook handlers. Conversion is over the built-in vocabulary; a $type outside it is dropped with a warning. What each target does with a component, and how unsupported content degrades, is documented on Generative UI on Slack, Generative UI on Microsoft Teams, and A2UI over AG-UI.

@assistant-ui/react-generative-ui/slack

Converts a tree to Slack Block Kit JSON (toSlackBlocks), decodes block_actions webhooks back into $action payloads (decodeBlockAction), and maps Block Kit back into vocabulary nodes (fromSlackBlocks).

import { toSlackBlocks } from "@assistant-ui/react-generative-ui/slack";

const { blocks, warnings } = toSlackBlocks({
  $type: "Card",
  title: "Order #48213",
  children: [{ $type: "Text", value: "Shipped, arriving Thursday." }],
});

@assistant-ui/react-generative-ui/teams

Converts a tree to a Microsoft Teams Adaptive Card (toAdaptiveCard) or bot-framework attachments with root-carousel support (toTeamsAttachments), and decodes an incoming activity.value (decodeSubmitData).

import { toAdaptiveCard } from "@assistant-ui/react-generative-ui/teams";

const { card, warnings } = toAdaptiveCard({
  $type: "Card",
  title: "Order #48213",
  children: [{ $type: "Text", value: "Shipped, arriving Thursday." }],
});

@assistant-ui/react-generative-ui/a2ui

The inbound direction: applies A2UI surface operations (applyA2uiOperations) and converts a surface into a vocabulary tree (convertSurfaceToUISpec) that renders through the same present path.

import {
  applyA2uiOperations,
  convertSurfaceToUISpec,
} from "@assistant-ui/react-generative-ui/a2ui";

const { state } = applyA2uiOperations(new Map(), operations);
for (const surface of state.values()) {
  const { spec, warnings } = convertSurfaceToUISpec(surface);
}

License

MIT