ComponentsAI & chat

Reasoning Effort

A pill-shaped slider that picks one of a few effort levels, such as Low, Medium, High, and Max. While you drag, a bubble grows out of the track and names the level.

Interactive effort selector

Drag the thumb, click a spot on the track, or use the arrow keys. A bubble with the level name grows out of the track while you drag, and the thumb snaps to the nearest level when you let go.

Selected level: Balanced

Composer model picker

The chat composer's model picker. The slider sits below the model list, the bubble shows the model name with the level, and picking a model keeps the panel open so you can set its effort.

Ask about your workspace…

Level counts

A model's level list sets the ticks. Ticks grow taller toward higher effort, and the tick under the fill turns light. One level still renders, with the thumb parked at the start.

Claude Opus · 4 levelsMax
DeepSeek Pro · 3 levelsMedium
Kimi · 2 levelsLow
Grok · 1 levelHigh

Usage

Reasoning Effort usageTSX
import { ReasoningEffort } from "@tulipfarm/sprout/reasoning-effort";

const levels = ["Quick", "Balanced", "Thorough", "Maximum"];
const [value, setValue] = useState(1);

<ReasoningEffort levels={levels} value={value} onValueChange={setValue} />

In a model picker, with the model name in the bubbleTSX
const effortLabel = { low: "Low", medium: "Medium", high: "High", max: "Max" };
const index = Math.max(0, model.efforts.indexOf(effort));

{model.efforts.length > 0 && (
  <ReasoningEffort
    label={model.label}
    levels={model.efforts.map((level) => effortLabel[level])}
    value={index}
    onValueChange={(next) => setEffort(model.efforts[next])}
  />
)}

The slider is controlled: pass the chosen index as value and update it in onValueChange. It callsonValueChange as soon as a drag crosses into a new level, not only on release. Hide it when a model has no levels.

Props

  • levels: level names from lowest to highest effort. Each one gets a tick, spaced evenly. Ticks grow taller toward higher effort.
  • value: the index of the chosen level.
  • onValueChange: called with the new index.
  • label: optional text before the level name in the bubble, such as the model name.
  • aria-label (default "Reasoning effort") oraria-labelledby names the slider.
  • className: added to the root.

The control is a fixed 256 × 52px: a 32px track with 20px above it for the bubble. It fits the 18rem picker popover with 16px from each outer edge: 15px of padding plus the popover's 1px border.

Accessibility

The track is one role="slider" tab stop. Its value is the index, and aria-valuetext reads the level name. Arrow keys move one level, and Home and End jump to the ends. Keyboard focus shows a 2px --sprout-focus outline.

The bubble is hidden from screen readers because the value text already names the level. When reduced motion is requested, the thumb, bubble, and level change jump instead of tweening. In forced colors mode the track, fill, thumb, and ticks use system colors.

Theme tokens

The fill uses --sprout-primary, so the opt-in TulipFarm theme tints it green. The pill and bubble shade from--sprout-hover to --sprout-active with a--sprout-line edge. The bubble's label uses--sprout-text and its level uses --sprout-muted. The thumb is white; override --sprout-reasoning-effort-thumbto change it.