Forty new components, one token layer
Koala UI 2 is out. Forty new components, a rebuilt token layer, four themes.
Set density once on the shell and every table, form and menu follows.
Upgrade with one command. Nothing in your app has to change.
The thing an agent makes, kept out of the thread. A compact card sits in the conversation; open it and a pane beside the chat shows the result, its versions and its code. Ask for a change and the card says so while the pane catches up.
Pro
$24/month
$288 billed yearly
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
export function PricingCard() {
return (
<div className="flex flex-col gap-4 rounded-xl p-5 ring-1 ring-edge">
<div className="flex items-center justify-between">
<p className="font-medium">Pro</p>
</div>
<p className="text-3xl font-semibold">$24<span>/month</span></p>
<p className="text-sm text-muted-foreground">$288 billed yearly</p>
<ul className="flex flex-col gap-2 text-sm">
<li>Every Pro component</li>
<li>Figma kit</li>
<li>Priority support</li>
</ul>
<Button className="w-full">Start free trial</Button>
</div>
)
}# One-time setup (tokens + lib helpers)
npx koalaui-cli@latest init
# Activate your Pro license (once per machine)
npx koalaui-cli@latest login <key>
# Add this component (its dependencies come along)
npx koalaui-cli@latest add artifactThe preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Two pieces that share a status. The card lives in the thread: an icon tile, a title over a short description, the version and an Open action. The pane lives beside it: a header with the title, the version stepper, the Preview / Code switch and the actions, over a content sheet that holds the preview, the code, the empty state or the skeleton.
import {
ArtifactCard,
ArtifactCardIcon,
ArtifactCardHeading,
ArtifactCardTitle,
ArtifactCardDescription,
ArtifactCardVersion,
ArtifactCardStatus,
ArtifactCardAction,
Artifact,
ArtifactHeader,
ArtifactTitle,
ArtifactVersion,
ArtifactTabs,
ArtifactActions,
ArtifactAction,
ArtifactCopy,
ArtifactContent,
ArtifactPreview,
ArtifactCode,
ArtifactEmpty,
ArtifactSkeleton,
} from "@/components/ui/artifact"
<ArtifactCard>
<ArtifactCardIcon />
<ArtifactCardHeading>
<ArtifactCardTitle />
<ArtifactCardDescription />
</ArtifactCardHeading>
<ArtifactCardStatus />
<ArtifactCardVersion />
<ArtifactCardAction />
</ArtifactCard>
<Artifact>
<ArtifactHeader>
<ArtifactTitle />
<ArtifactVersion />
<ArtifactTabs />
<ArtifactActions>
<ArtifactCopy />
<ArtifactAction />
</ArtifactActions>
</ArtifactHeader>
<ArtifactContent>
<ArtifactSkeleton />
<ArtifactPreview />
<ArtifactCode />
<ArtifactEmpty />
</ArtifactContent>
</Artifact>Four states, and they all live where your eye already is. The icon tile cross-fades to a spinner while the agent works and to a warning when it gives up. While generating, the title sweeps and the description holds a shimmer bar in its own footprint, so the card does not grow when the words arrive. While updating, the version chip opens to v3 → v4: the number you know stays put and only the new one is added.
Play the lifecycle once to see the hand-offs: generating, ready, updating, then ready on the next version.
type picks the glyph and the fallback description: document, code, app, chart or image. Pass your own glyph as the icon's children for anything else.
The card and the pane share nothing but your state, which keeps them free to live in different trees: the card inside a message, the pane in a split, a sheet or an AI Panel. Hold one open flag and one status per artifact. The card's action sets it, the pane's close clears it, and open on the card keeps it lifted while its pane is showing so you can tell which card the pane belongs to. Send a change from the composer below: the card switches to updating, the pane dims and sweeps its title, then follows to the new version on its own.
Pro
$24/month
$288 billed yearly
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
export function PricingCard() {
return (
<div className="flex flex-col gap-4 rounded-xl p-5 ring-1 ring-edge">
<div className="flex items-center justify-between">
<p className="font-medium">Pro</p>
</div>
<p className="text-3xl font-semibold">$24<span>/month</span></p>
<p className="text-sm text-muted-foreground">$288 billed yearly</p>
<ul className="flex flex-col gap-2 text-sm">
<li>Every Pro component</li>
<li>Figma kit</li>
<li>Priority support</li>
</ul>
<Button className="w-full">Start free trial</Button>
</div>
)
}Each request makes a version, and earlier ones stay one step away. Stepping back slides the content right, stepping forward slides it left, and the number in the read-out rolls the same way, so you always know which direction you went. Pass history and the read-out opens a menu of every version with a line about what changed.
Koala UI 2 is out. Forty new components, a rebuilt token layer, four themes.
Set density once on the shell and every table, form and menu follows.
Upgrade with one command. Nothing in your app has to change.
Add ArtifactTabs and the preview and the code become two views of one sheet. Leave it out and whichever parts you render simply show, so a document needs no tabs at all. For a live app, pass src to the preview and it renders a sandboxed iframe that fades in once it has loaded.
import { Button } from "@/components/ui/button"
import { InputField, InputRoot } from "@/components/ui/input"
export function WaitlistForm() {
return (
<form className="flex w-full max-w-sm flex-col gap-3">
<p className="font-medium">Join the Koala beta</p>
<InputRoot>
<InputField type="email" placeholder="you@company.com" />
</InputRoot>
<Button type="submit">Request access</Button>
</form>
)
}Before anything exists, ArtifactEmpty tells the reader what the pane is for. While the first version is generating, ArtifactSkeleton takes the sheet with shimmer lines in the shape of a page; the preview and code wait for ready, so you can leave all three in the markup and let the status decide.
Ask for a document, a component or a chart and it opens in this pane.
Inside an AI Panel the panel is already the frame, so use variant="plain": the tint goes and the header sits on the body's own inset, while the content keeps its sheet so the result still reads as one object. Give it a height and it scrolls inside, under the panel's composer.
Koala UI 2 is out. Forty new components, a rebuilt token layer, four themes.
Set density once on the shell and every table, form and menu follows.
Upgrade with one command. Nothing in your app has to change.
The in-thread card: what was made, its version and an Open action. The context every card part reads. Forwards native <div> props. Every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| status | "generating" | "ready" | "updating" | "error" | "ready" | Drives the icon column, the title's sweep, the description skeleton, the version chip and aria-busy (generating or updating). |
| type | "document" | "code" | "app" | "chart" | "image" | "document" | Picks the glyph in ArtifactCardIcon and the fallback wording of ArtifactCardDescription. |
| version | number | - | The latest version, shown by ArtifactCardVersion. Leave it out while the first version is still generating. |
| open | boolean | false | The card's pane is showing. The card stays lifted and its glyph takes full ink. |
| Data attribute | Description |
|---|---|
| data-status | The card's status. |
| data-type | The card's type. |
| data-open | Present while open. |
The 40px tile. Shows the type's glyph (or your children) and cross-fades to a spinner while generating or updating and a warning on error. aria-hidden. Renders a <span>.
Stacks the title over the description and takes the free width. Renders a <div>.
What was made. Sweeps while generating or updating, and names the Open action. Renders a <span>.
A short line under the title, the type's name by default. While generating it holds a shimmer bar in the same footprint; on error it turns destructive. Renders a <span>.
A Badge reading v3. While updating, → v4 opens beside it, so the number you know stays put. Renders nothing without a version. Forwards Badge props.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | - | Your own wording instead of the version. |
An optional chip worded and toned from status: Generating, Ready, Updating or Failed. Forwards Badge props.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | BadgeProps["variant"] | - | Overrides the tone the status picks. |
| children | React.ReactNode | - | Overrides the wording. |
The Open button. Its hit area stretches over the whole card, its focus ring moves to the card, and its accessible name includes the title ("Open Pricing card"). Forwards Button props.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | "Open" | The label, "Retry" on a failed card for instance. |
| variant | ButtonProps["variant"] | "ghost" | Button variant. |
The pane. Holds the version, the Preview / Code view and the status every pane part reads. A Koala Tabs root underneath, so it forwards its props.
| Prop | Type | Default | Description |
|---|---|---|---|
| status | "generating" | "ready" | "updating" | "error" | "ready" | generating shows ArtifactSkeleton and holds the preview and code back; updating sweeps the title and dims the content. |
| variant | "card" | "plain" | "card" | card is the tinted frame with the content as a sheet set 8px in; plain drops the frame for an AI Panel or a page that already frames it. |
| density | "comfortable" | "compact" | - | Header inset and preview padding. Inherits the nearest DensityProvider (compact with none). |
| versions | number | 1 | How many versions exist. When it grows and you were on the latest, the pane follows to the new one. |
| version | number | - | Controlled version, 1-based. |
| defaultVersion | number | - | Initial version. The latest by default. |
| onVersionChange | (version: number) => void | - | Called when the stepper or the history picks a version. |
| view | "preview" | "code" | - | Controlled view, when ArtifactTabs is present. |
| defaultView | "preview" | "code" | "preview" | Initial view. |
| onViewChange | (view: "preview" | "code") => void | - | Called when the tabs switch view. |
| Data attribute | Description |
|---|---|
| data-status | The pane's status. |
The bar above the content. Wraps to a second line on a narrow pane. Renders a <div>.
The artifact's name. Sweeps while generating or updating. Renders a <div>.
Previous and next around a "3 of 4" read-out in tabular-nums. The number rolls in the direction you stepped and the content slides the same way. Renders a role="group" <div>.
| Prop | Type | Default | Description |
|---|---|---|---|
| history | { label: React.ReactNode; description?: React.ReactNode }[] | - | One entry per version, oldest first. Turns the read-out into a menu that jumps to any version, newest on top. |
| previousLabel | string | "Previous version" | Accessible name and tooltip of the back step. |
| nextLabel | string | "Next version" | Accessible name and tooltip of the forward step. |
| historyLabel | string | "Version history" | Accessible name of the group and the menu trigger. |
The trailing row of icon actions, pushed to the end of the header. Renders a <div>.
A muted icon button with its aria-label as tooltip: download, share, close. Forwards Button props.
| Prop | Type | Default | Description |
|---|---|---|---|
| aria-label | string | - | Required: the name and the tooltip. |
An ArtifactAction that writes value to the clipboard and turns its glyph into a check for a moment.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | (() => string) | - | What to copy. Pass a function to read the current version at click time. |
| copyLabel | string | "Copy" | Name and tooltip at rest. |
| copiedLabel | string | "Copied" | Name and tooltip once copied. |
The Preview / Code switch, a segmented Koala TabsList. With it present, ArtifactPreview and ArtifactCode become its panels; without it, both simply render.
| Prop | Type | Default | Description |
|---|---|---|---|
| previewLabel | React.ReactNode | "Preview" | First tab's label. |
| codeLabel | React.ReactNode | "Code" | Second tab's label. |
| children | React.ReactNode | - | Your own TabsTriggers, valued preview and code. |
The sheet. Keys its children by version: the old version leaves toward where you came from and the new one arrives from where you are going. Renders a <div>.
The live result, scrolling inside the sheet. Renders nothing while generating. Forwards native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | - | Render a sandboxed <iframe> at this URL instead of children. It fades in once it loads. |
| title | string | "Artifact preview" | The iframe's accessible title. |
| sandbox | string | "allow-scripts allow-forms allow-popups" | The iframe's sandbox flags. |
The source, as a flat CodeSnippet with line numbers and copy on hover. Renders nothing while generating.
| Prop | Type | Default | Description |
|---|---|---|---|
| code | string | - | The source. |
| lang | CodeSnippetProps["lang"] | "tsx" | Highlighting language. |
| showLineNumbers | boolean | true | Show the gutter. |
| snippetProps | Partial<CodeSnippetProps> | - | Anything else for the inner CodeSnippet. |
The pane before anything exists, a compact EmptyState. Forwards EmptyState props; children replace the default parts.
| Prop | Type | Default | Description |
|---|---|---|---|
| icon | React.ReactNode | <Sparkle /> | The tile's glyph. |
| title | React.ReactNode | "Your artifact appears here" | The message. |
| description | React.ReactNode | - | The line under it. |
The building state: shimmer lines in the shape of a page, staggered in. Renders only while the pane is generating, with role="status".
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | "Building the artifact" | What a screen reader hears. |
| children | React.ReactNode | - | Your own skeleton, shaped like the artifact you expect. |