AI Chat Fancy Stable
PromptSuggestions
Row of prompt pills that cascade in after a reply lands, offering the user a next turn without composing one
Preview
svelte
<script lang="ts">
import { onMount } from "svelte";
import { PromptSuggestions } from "fancy-ui-svelte";
const SUGGESTIONS = [
"Why was the lockfile stale?",
"Show me the failing job",
"Turn the cache back on",
"Summarize this for the team",
];
/** How long the reply sits alone before the follow-ups offer themselves. */
const APPEAR_MS = 700;
/** The beat the pills spend hidden before replaying, after one is picked. */
const REPLAY_MS = 260;
let visible = $state(false);
let turns = $state<string[]>([]);
let timer: ReturnType<typeof setTimeout> | undefined;
let reduced = false;
onMount(() => {
reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
// Nothing to stage when motion is reduced: the follow-ups are simply there,
// which is the state the delay was building towards anyway.
if (reduced) {
visible = true;
return;
}
timer = setTimeout(() => (visible = true), APPEAR_MS);
return () => clearTimeout(timer);
});
function select(suggestion: string) {
turns = [...turns, suggestion];
// Hiding and re-showing is what replays the cascade — worth seeing once you
// have picked one, but only when motion is welcome.
if (reduced) return;
visible = false;
clearTimeout(timer);
timer = setTimeout(() => (visible = true), REPLAY_MS);
}
</script>
<div class="flex w-full max-w-xl flex-col gap-3">
<div class="bg-muted text-foreground mr-auto max-w-[85%] rounded-2xl rounded-bl-sm px-4 py-3">
<p class="text-sm leading-relaxed">
The build is green again. The failure came from a stale lockfile in the CI cache rather than
from anything in your branch — I re-ran the job with the cache disabled and every suite passed
on the first attempt.
</p>
</div>
{#each turns as turn, i (i)}
<div
class="bg-primary text-primary-foreground ml-auto max-w-[85%] rounded-2xl rounded-br-sm px-4 py-2.5"
>
<p class="text-sm">{turn}</p>
</div>
{/each}
<PromptSuggestions
suggestions={SUGGESTIONS}
{visible}
onSelect={select}
label="Follow-up prompts"
class="mt-1"
/>
</div>Installation
pnpm add fancy-ui-svelte
import { PromptSuggestions } from 'fancy-ui-svelte'
Usage
svelte
<script lang="ts">
import { PromptSuggestions } from 'fancy-ui-svelte';
</script>
<PromptSuggestions />Examples
Basic Usage
svelte
<script lang="ts">
import { onMount } from "svelte";
import { PromptSuggestions } from "$lib/fancy-ui/prompt-suggestions";
const SUGGESTIONS = [
"Why was the lockfile stale?",
"Show me the failing job",
"Turn the cache back on",
"Summarize this for the team",
];
/** How long the reply sits alone before the follow-ups offer themselves. */
const APPEAR_MS = 700;
/** The beat the pills spend hidden before replaying, after one is picked. */
const REPLAY_MS = 260;
let visible = $state(false);
let turns = $state<string[]>([]);
let timer: ReturnType<typeof setTimeout> | undefined;
let reduced = false;
onMount(() => {
reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
// Nothing to stage when motion is reduced: the follow-ups are simply there,
// which is the state the delay was building towards anyway.
if (reduced) {
visible = true;
return;
}
timer = setTimeout(() => (visible = true), APPEAR_MS);
return () => clearTimeout(timer);
});
function select(suggestion: string) {
turns = [...turns, suggestion];
// Hiding and re-showing is what replays the cascade — worth seeing once you
// have picked one, but only when motion is welcome.
if (reduced) return;
visible = false;
clearTimeout(timer);
timer = setTimeout(() => (visible = true), REPLAY_MS);
}
</script>
<div class="flex w-full max-w-xl flex-col gap-3">
<div class="bg-muted text-foreground mr-auto max-w-[85%] rounded-2xl rounded-bl-sm px-4 py-3">
<p class="text-sm leading-relaxed">
The build is green again. The failure came from a stale lockfile in the CI cache rather than
from anything in your branch — I re-ran the job with the cache disabled and every suite passed
on the first attempt.
</p>
</div>
{#each turns as turn, i (i)}
<div
class="bg-primary text-primary-foreground ml-auto max-w-[85%] rounded-2xl rounded-br-sm px-4 py-2.5"
>
<p class="text-sm">{turn}</p>
</div>
{/each}
<PromptSuggestions
suggestions={SUGGESTIONS}
{visible}
onSelect={select}
label="Follow-up prompts"
class="mt-1"
/>
</div>Props
| Prop | Type | Default | Description |
|---|---|---|---|
suggestions * | string[] | - | Prompt texts offered to the user, in display order |
onSelect | (suggestion: string, index: number) => void | - | Called with the chosen prompt and its index when a pill is activated |
visible | boolean | true | Whether the pills are shown; flipping this to true replays the entrance |
staggerMs | number | 60 | Delay between two consecutive pills entering, in milliseconds |
label | string | "Suggestions" | Accessible name of the group wrapping the pills |
Slots
| Slot | Description |
|---|---|
item | Custom pill content, receiving the suggestion and its index |
Links
Related components
Ch
ChatError
Quiet inline failure banner for a chat turn: the error, an optional detail line, and a retry button that disables itself while the retry is in flight
AI Chat Stable
Ch
ChatMessage
One conversation turn, aligned and dressed by its role, streaming its body while the answer arrives, with an action rail that fades in on hover and a version navigator underneath
AI Chat Stable
Ch
ChatPanel
The shell a conversation lives in: a sticky header, a transcript that pins itself to the bottom while an answer arrives and offers the way back once you scroll up, and a sticky composer row
AI Chat Stable