othmanadi/openui-forge · Archived

openui-forge-anthropic

OpenUI generative UI with Anthropic Claude SDK backend. Stream conversion to OpenAI NDJSON format.

First seen May 20, 2026

Installation

$ npx skills add othmanadi/openui-forge --skill openui-forge-anthropic

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from othmanadi/openui-forge · top by installs.

npx skills add othmanadi/openui-forge

Browse all from othmanadi/openui-forge

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 22
License LICENSE
Default branch main
Open issues 0
Status Archived

Skill metadata

Parsed from SKILL.md frontmatter.

Version1.2.0
Declared agents claude-code

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 6,011 B
  • docs SUMMARY.md 128 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 13 installs

SKILL.md

OpenUI Forge — Anthropic

Build generative UI apps with OpenUI + Anthropic Claude. Converts Anthropic streaming events to OpenAI-compatible NDJSON.

Activation Triggers

  • "openui anthropic", "openui claude", "openui sonnet"
  • "generative ui claude", "claude streaming ui"

Prerequisites

  • Node.js >= 22 (24 LTS recommended), React >= 18.3.1 (19+ recommended)
  • ANTHROPICAPIKEY environment variable set
  • Next.js project (App Router recommended)

Quick Start

  1. Install dependencies:
npm install @openuidev/react-ui @openuidev/react-headless @openuidev/react-lang lucide-react zod @anthropic-ai/sdk
  1. Add the CSS import to app/layout.tsx:
import "@openuidev/react-ui/components.css";
  1. Create the API route and frontend page below
  2. Run npm run dev and test

Full Code

Backend: app/api/chat/route.ts

The backend streams from Anthropic and converts each event into OpenAI-compatible SSE chunks that openAIAdapter() expects (data: {json}\n\n lines, terminated by data: [DONE]).

import { openuiChatLibrary } from "@openuidev/react-ui/genui-lib";
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

export async function POST(req: Request) {
  const { messages } = await req.json();

  const systemPrompt = openuiChatLibrary.prompt({
    preamble: "You are a helpful assistant that generates interactive UIs.",
    additionalRules: ["Always use Stack as root when combining multiple components."],
  });

  // ANTHROPIC_MODEL alternatives: claude-opus-4-8, claude-haiku-4-5, claude-fable-5
  const stream = client.messages.stream({
    model: process.env.ANTHROPIC_MODEL ?? "claude-sonnet-4-6",
    max_tokens: 4096,
    system: systemPrompt,
    messages,
  });

  const encoder = new TextEncoder();
  const readableStream = new ReadableStream({
    async start(controller) {
      const id = `chatcmpl-${Date.now()}`;
      for await (const event of stream) {
        if (
          event.type === "content_block_delta" &&
          event.delta.type === "text_delta"
        ) {
          const chunk = {
            id,
            object: "chat.completion.chunk",
            choices: [
              {
                index: 0,
                delta: { content: event.delta.text },
                finish_reason: null,
              },
            ],
          };
          controller.enqueue(
            encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`)
          );
        }
      }
      const done = {
        id,
        object: "chat.completion.chunk",
        choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
      };
      controller.enqueue(encoder.encode(`data: ${JSON.stringify(done)}\n\n`));
      controller.enqueue(encoder.encode("data: [DONE]\n\n"));
      controller.close();
    },
  });

  return new Response(readableStream, {
    headers: { "Content-Type": "text/event-stream" },
  });
}

Frontend: app/chat/page.tsx

"use client";
import { FullScreen } from "@openuidev/react-ui";
import { openuiChatLibrary } from "@openuidev/react-ui/genui-lib";
import {
  openAIAdapter,
  openAIMessageFormat,
} from "@openuidev/react-headless";

export default function ChatPage() {
  return (
    <FullScreen
      componentLibrary={openuiChatLibrary}
      streamProtocol={openAIAdapter()}
      messageFormat={openAIMessageFormat}
      apiUrl="/api/chat"
    />
  );
}

The backend emits SSE (data: {json}\n\n). Pair it with openAIAdapter() on the frontend — openAIReadableStreamAdapter() is for NDJSON (no data: prefix) and will silently produce no output here.

Component Creation

import { defineComponent } from "@openuidev/react-lang";
import { z } from "zod";

export const StatusCard = defineComponent({
  name: "StatusCard",
  description: "Displays a status with label and color indicator",
  props: z.object({
    label: z.string().describe("Status label text"),
    status: z.enum(["ok", "warning", "error"]).describe("Current status level"),
  }),
  component: ({ props }) => {
    const colors = { ok: "#22c55e", warning: "#eab308", error: "#ef4444" };
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
        <span style={{ width: 10, height: 10, borderRadius: "50%", background: colors[props.status] }} />
        <span>{props.label}</span>
      </div>
    );
  },
});

System Prompt Generation

npx @openuidev/cli generate ./src/lib/library.ts --out src/generated/system-prompt.txt

Or at runtime: openuiChatLibrary.prompt({ preamble: "...", additionalRules: [...] }).

Validation Checklist

  • ANTHROPICAPIKEY is set in .env.local
  • CSS import present in root layout
  • Backend converts Anthropic contentblockdelta events to OpenAI-compatible SSE chunks
  • Final chunk has finish_reason: "stop" and ends with data: [DONE]
  • Frontend uses streamProtocol={openAIAdapter()} and openAIMessageFormat
  • componentLibrary={openuiChatLibrary} prop passed to FullScreen

Error Patterns

Error Cause Fix
401 from Anthropic Missing or invalid API key Set ANTHROPICAPIKEY in .env.local
Stream hangs Missing [DONE] sentinel or controller.close() Ensure final chunk and [DONE] are sent
Garbled output Not wrapping in data: ... SSE format Each chunk must be data: {json}\n\n
Components render as text Library not passed to FullScreen Add componentLibrary={openuiChatLibrary} prop
Nothing renders, no error Used openAIReadableStreamAdapter() (NDJSON) on SSE stream, or adapter= prop (silently ignored) Use streamProtocol={openAIAdapter()}
max_tokens required Anthropic API requires explicit max_tokens Always set max_tokens (e.g., 4096)