AI & Chat

Suggestion Chips

Starter prompt chips that enter with a 40ms-staggered blur-rise, lift on hover, and hand the clicked prompt to the composer.

Install

npx shadcn@latest add @paragon/suggestion-chips

Also installs: prompt-input

suggestion-chips.tsx

"use client";

import * as React from "react";
import { motion, useReducedMotion } from "motion/react";
import { cn } from "@/lib/utils";

export interface SuggestionChipsProps
  extends Omit<React.ComponentProps<"div">, "onSelect"> {
  /** Starter prompts, rendered in order. */
  suggestions: string[];
  /** Called with the clicked suggestion — wire it to your composer. */
  onSelect?: (suggestion: string) => void;
  /** Seconds between each chip's entrance. */
  stagger?: number;
  /** Disables the entrance and press motion. */
  static?: boolean;
}

/**
 * Starter prompt chips. They enter once with a 40ms-staggered blur-rise,
 * lift to shadow-border-hover on hover, and hand the clicked prompt to
 * `onSelect` so it can fill the composer.
 */
export function SuggestionChips({
  suggestions,
  onSelect,
  stagger = 0.04,
  static: isStatic = false,
  className,
  ...props
}: SuggestionChipsProps) {
  const reducedMotion = useReducedMotion();

  return (
    <div
      data-slot="suggestion-chips"
      className={cn("flex flex-wrap gap-2", className)}
      {...props}
    >
      {suggestions.map((suggestion, i) => (
        <motion.button
          key={suggestion}
          type="button"
          onClick={() => onSelect?.(suggestion)}
          initial={
            isStatic
              ? false
              : reducedMotion
                ? { opacity: 0 }
                : { opacity: 0, y: 12, filter: "blur(4px)" }
          }
          animate={
            reducedMotion
              ? { opacity: 1 }
              : { opacity: 1, y: 0, filter: "blur(0px)" }
          }
          transition={{
            type: "spring",
            duration: 0.3,
            bounce: 0,
            delay: isStatic ? 0 : i * stagger,
          }}
          className={cn(
            "h-8 rounded-full bg-card px-3.5 text-[13px] text-muted-foreground shadow-border transition-[box-shadow,color,scale] duration-150 ease-out hover:text-foreground hover:shadow-border-hover",
            !isStatic && "active:not-disabled:scale-[0.97]",
          )}
        >
          {suggestion}
        </motion.button>
      ))}
    </div>
  );
}