Skip to content
Tier

Artifact toolbar

Pro

The actions on an artifact or a dashboard tile: maximise and restore, download (one format a button, several a menu), and "Ask about this", which hands the artifact to the app's chat (Candor attaches nothing itself). Each button names the artifact, so a page of tiles doesn't read "Download, Download, Download". A control whose handler isn't given isn't drawn; one that can't be used now stays, locked, saying why. It is one tab stop: the arrow keys move between its buttons (ArtifactToolbarButton for your own).

Markdown
Version 5.2.0· other versions

This page is for 5.2.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).

Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0, 2.5.1, 2.6.0, 2.6.1, 2.6.2, 2.7.0, 2.8.0, 2.9.0, 2.10.0, 3.0.0, 4.0.0, 4.0.1, 4.0.2 and 4.1.0: the page came later, or had another name then.

Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.

Preview

Loading the preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/artifact-toolbar

Brings in: button, dropdown-menu, locale, locked, tokens, utils · npm: @base-ui/react@^1.9.0, lucide-react@^1.67.0

Usage

TSX · The example above
"use client"

import * as React from "react"
import { Share2 } from "lucide-react"
import { ArtifactToolbar, ArtifactToolbarButton } from "@/components/candor/artifact-toolbar"
import { TooltipProvider } from "@/components/candor/ui/tooltip"

export default function Example() {
  const [maximised, setMaximised] = React.useState(false)
  const [last, setLast] = React.useState("Nothing yet.")
  return (
    <TooltipProvider>
      <div className="flex max-w-md flex-col gap-2 rounded-xl border bg-card p-4">
        <div className="flex items-center gap-2">
          <p className="flex-1 text-sm font-semibold">Failed runs this week</p>
          <ArtifactToolbar
            title="Failed runs this week"
            maximised={maximised}
            onMaximisedChange={setMaximised}
            downloads={[{ format: "csv", label: "CSV" }, { format: "png", label: "PNG image" }]}
            onDownload={(format) => setLast(`Download as ${format}`)}
            onAsk={() => setLast("Chat opened with the table attached")}
          >
            <ArtifactToolbarButton aria-label="Share Failed runs this week" onClick={() => setLast("Share")}>
              <Share2 aria-hidden />
            </ArtifactToolbarButton>
          </ArtifactToolbar>
        </div>
        <p className="text-xs text-muted-foreground">{last}</p>
      </div>
    </TooltipProvider>
  )
}

<ArtifactToolbar>

PropTypeDefaultDescription
titlerequiredstring—The artifact's name, in every button's accessible name: "Download Failed runs".
maximisedboolean | undefinedfalseWhether the artifact is maximised now: the button's name is then Restore rather than Maximise.
onMaximisedChange((maximised: boolean) => void) | undefined—Called with the state the person asks for. Without it, there's no maximise button.
downloadsArtifactDownload[] | undefined—The formats it downloads as: one is a button, more a menu.
onDownload((format: string) => void) | undefined—Called with the format the person picks; the app makes the file. Without it, there's no download.
onAsk(() => void) | undefined—Opens the app's chat with this artifact attached ("Ask about this"). Without it, there's no ask button.
locked{ [K in ArtifactToolbarControl]?: string | null | undefined } | undefined—Why a control can't be used now, by control. It stays focusable, marked `aria-disabled`, and says why. A control whose reason is `null` or left out is usable.
childrenReact.ReactNode | undefined—More actions, as `ArtifactToolbarButton`s, after the built-in ones.
classNamestring | undefined—Classes for the root, merged with Candor's.

<ArtifactToolbarButton>

One more action in an ArtifactToolbar, so the arrow keys reach it: a small ghost button. Name it, with the artifact's title, in aria-label when it shows only an icon.

PropTypeDefaultDescription
classNamestring | undefined—Classes for the button, merged with Candor's.

Also takes the props of Omit<ToolbarPrimitive.Button.Props, "className" | "render">.

Strings

The fixed words, in English by default. Change any of them under artifactToolbar in <LocaleProvider strings={{ … }}>; the table is exported as artifactToolbarStrings.

KeyEnglish
toolbarActions for {title}
maximiseMaximise {title}
restoreRestore {title}
downloadDownload {title}
downloadAsDownload {title} as {format}
askAsk about this: {title}
askShortAsk about this

Types

ArtifactDownload
One format the artifact downloads as: its id, passed to `onDownload`, and the words for it ("CSV").
{ /** Passed to `onDownload`: "csv", "png". */ format: string; /** The format's words; defaults to `format`. */ label?: string | undefined; }
ArtifactToolbarControl
The controls a reason can lock.
"maximise" | "download" | "ask"