Starter prompt chips that enter with a 40ms-staggered blur-rise, lift on hover, and hand the clicked prompt to the composer.
npx shadcn@latest add @paragon/suggestion-chipsAlso installs: prompt-input
"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>
);
}