Skip to content
Fabrik UI

Components

Chat UI components and defineComponent for generative UI

Chat UI

Fabrik ships two drop-in chat surfaces:

Full-page chat

app/page.tsx
import { Fabrik, Chat } from "@fabrik-sdk/ui/react"

export default function Page() {
  return (
    <Fabrik provider={provider}>
      <Chat />
    </Fabrik>
  )
}

<Chat> renders a full message list with input area, auto-scroll, typing indicators, and elicitation UI. Pass sidebar to enable a thread sidebar.

Props

PropTypeDescription
sidebarbooleanShow thread sidebar
placeholderstringInput placeholder text
welcomeReactNodeCustom empty state
classNamestringContainer class
onFeedback(messageId, type) => voidThumbs up/down callback

Floating action button

app/page.tsx
import { Fabrik, Fab } from "@fabrik-sdk/ui/react"

export default function Page() {
  return (
    <Fabrik provider={provider}>
      <Fab />
    </Fabrik>
  )
}

<Fab> renders a floating button in the corner that expands into a chat panel. Ideal for copilot and widget patterns.

defineComponent

Register React components the LLM can render:

components/stock-chart.tsx
import { defineComponent } from "@fabrik-sdk/ui"
import { z } from "zod"

export const stockChart = defineComponent({
  name: "stock_chart",
  description: "Displays stock price history as a line chart",
  schema: z.object({
    symbol: z.string(),
    prices: z.array(z.object({
      date: z.string(),
      price: z.number(),
    })),
    change: z.number(),
  }),
  component: ({ symbol, prices, change }) => (
    <div className="rounded-xl border p-4">
      <div className="flex items-center justify-between">
        <h3 className="font-bold">{symbol}</h3>
        <span className={change >= 0 ? "text-green-600" : "text-red-600"}>
          {change >= 0 ? "+" : ""}{change}%
        </span>
      </div>
      {/* Render your chart here */}
    </div>
  ),
})

Fields

FieldTypeRequiredDescription
namestringYesTool name sent to the LLM (use snake_case)
descriptionstringYesTells the LLM when to use this component
schemaZodType<T>YesProps schema — converted to JSON Schema for the LLM
componentComponentType<T>YesReact component that renders with validated props
loadingComponentType<Partial<T>>NoShown while props are streaming
stepTitlestring | (args: T) => stringNoLabel in the step indicator

Passing components to Fabrik

app/page.tsx
import { Fabrik, Chat } from "@fabrik-sdk/ui/react"
import { weatherCard } from "@/components/weather-card"
import { stockChart } from "@/components/stock-chart"

export default function Page() {
  return (
    <Fabrik provider={provider} components={[weatherCard, stockChart]}>
      <Chat />
    </Fabrik>
  )
}

createLibrary

Group components into reusable collections:

lib/components.ts
import { createLibrary } from "@fabrik-sdk/ui"
import { weatherCard } from "@/components/weather-card"
import { stockChart } from "@/components/stock-chart"
import { dataTable } from "@/components/data-table"

export const components = createLibrary([
  weatherCard,
  stockChart,
  dataTable,
])

createLibrary validates that all component names are unique and returns a frozen array.

useChat hook

Build custom chat UI with the useChat hook:

components/custom-chat.tsx
"use client"
import { useChat } from "@fabrik-sdk/ui/react"

export function CustomChat() {
  const { messages, input, send, isLoading, status } = useChat()

  return (
    <div>
      {messages.map((msg) => (
        <div key={msg.id}>{msg.role}: {msg.parts.length} parts</div>
      ))}
      <input
        value={input.value}
        onChange={(e) => input.set(e.target.value)}
        onKeyDown={(e) => e.key === "Enter" && send()}
      />
    </div>
  )
}

Return values

FieldTypeDescription
messagesFabrikMessage[]All messages in the current thread
isLoadingbooleanTrue while streaming or waiting
status"idle" | "streaming" | "waiting" | "error"Thread status
input.valuestringCurrent input text
input.set(value: string) => voidUpdate input
send() => voidSend the current input
cancel() => voidCancel the active stream
retry() => voidRetry the last user message
respond(askId, value) => voidRespond to an elicitation
threadIdstringCurrent thread ID
newThread() => voidStart a new thread
switchThread(id: string) => voidSwitch to another thread

On this page