0.0.4 • Published 3 months ago
jsx2ui
Licence
—
Version
0.0.4
Deps
3
Size
126 kB
Vulns
0
Weekly
0
jsx2ui
jsx2ui helps you integrate generative, model-authored UI into your app.
It turns JSX returned by your model tool calls into serializable UI JSON and renders it with your own React component catalog.
Installation
npm install jsx2ui
Table of Contents
- Package Entrypoints
- Quick Start (AI SDK + Cloudflare Workers)
- Server-side APIs
- Frontend APIs (React)
Package Entrypoints
| Entrypoint | Layer | Use for |
|---|---|---|
jsx2ui |
Core | Framework-agnostic primitives like schema/prompt generation and JSX-to-JSON transformation |
jsx2ui/ai-sdk |
Server | AI SDK createUI tool and createGenUI helper |
jsx2ui/react |
Frontend | Shared React primitives (createCatalog, Renderer, result guards/types) |
jsx2ui/react/ai-sdk |
Frontend | AI SDK message-part rendering utilities |
Quick Start (AI SDK + Cloudflare Workers)
1) Define the components in frontend
// src/catalog.tsx
import { createCatalog } from "jsx2ui/react";
function Title({ children, size }: { children: React.ReactNode; size: "sm" | "md" | "lg" }) {
return <h1 className={`text-${size}`}>{children}</h1>;
}
function Card({ children }: { children: React.ReactNode }) {
return <div className="card">{children}</div>;
}
const catalog = createCatalog({
Title,
Card,
});
2) Define the components schema for the LLM
import { defineComponents, z } from "jsx2ui";
export const components = defineComponents([
{
name: "Title",
acceptsChildren: true,
props: z.object({
size: z.enum(["sm", "md", "lg"]).default("md"),
}),
},
{
name: "Card",
acceptsChildren: true,
},
]);
3) Add the UI tool to your AI chat configuration
import { createWorkersAI } from "workers-ai-provider";
import {
createUIMessageStream,
createUIMessageStreamResponse,
convertToModelMessages,
streamText,
} from "ai";
import { createGenUI } from "jsx2ui/ai-sdk";
import { components } from "./schema";
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname.startsWith("/api/chat")) {
const { messages } = await request.json();
const workersAi = createWorkersAI({ binding: env.AI });
const genUI = createGenUI({ loader: env.LOADER, components });
const modelMessages = await convertToModelMessages(messages);
const stream = createUIMessageStream({
execute: ({ writer }) => {
const textStream = streamText({
model: workersAi("@cf/zai-org/glm-4.7-flash"),
system: genUI.systemPrompt(),
messages: modelMessages,
tools: genUI.tools(writer),
stopWhen: [genUI.stopCondition()],
});
writer.merge(textStream.toUIMessageStream());
},
});
return createUIMessageStreamResponse({ stream });
}
return new Response(null, { status: 404 });
},
} satisfies ExportedHandler<Env>;
4) Render the UI on the frontend
// src/chat.tsx
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
import { useGenUI } from "jsx2ui/react/ai-sdk";
import { catalog } from "./catalog";
function Chat() {
const { parsePart, onData } = useGenUI({ catalog });
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({ api: "/api/chat" }),
onData,
});
return (
<>
{messages.map((message) => (
<div key={message.id}>
{message.parts.map((part, partIdx) => {
const result = parsePart(part);
if (result.handled) {
return <div key={partIdx}>{result.element}</div>;
}
if (part.type === "text") {
return <span key={partIdx}>{part.text}</span>;
}
return null;
})}
</div>
))}
</>
);
}
5) Styling for the default components (Optional - if using TailwindCSS)
For TailwindCSS v4:
/* src/index.css */
@source "../node_modules/jsx2ui/dist/**/*.*js";
For TailwindCSS v3:
// tailwind.config.js
module.exports = {
content: [
"./node_modules/jsx2ui/dist/**/*.*js", // add this to content
],
};
Server-side APIs
Core framework-agnostic APIs (jsx2ui)
import { defineComponents, z } from "jsx2ui";
const genUI = defineComponents([
{
name: "Title",
acceptsChildren: true,
props: z.object({
size: z.enum(["sm", "md", "lg"]).default("md"),
}),
},
{ name: "Card", acceptsChildren: true },
]);
console.log(genUI.getSystemPrompt());
AI SDK server API (jsx2ui/ai-sdk)
import { streamText } from "ai";
import { createGenUI } from "jsx2ui/ai-sdk";
const genUI = createGenUI({ loader: env.LOADER, components });
const result = streamText({
model,
system: genUI.systemPrompt(),
messages,
tools: genUI.tools(),
stopWhen: genUI.stopCondition(),
});
Frontend APIs (React)
Shared React APIs (jsx2ui/react)
import React from "react";
import { createCatalog, Renderer, type UINode } from "jsx2ui/react";
const catalog = createCatalog({ Title, Card });
export function GeneratedView({ ui }: { ui: UINode }) {
return <Renderer node={ui} catalog={catalog} />;
}
AI SDK React APIs (jsx2ui/react/ai-sdk)
import React from "react";
import { isGenUiPart, useGenUI } from "jsx2ui/react/ai-sdk";
const { parsePart, reset } = useGenUI({ catalog });
// Optional guard when filtering AI SDK message parts yourself.
if (isGenUiPart(part)) {
const result = parsePart(part);
return <>{result.element}</>;
}
// Call reset() when removing chat messages to release cached generated UI state.