Elements

07 / 122 · Reasoning

Guardrail notice

A refusal in its own shape, with the nearest thing it can do instead.

I can't help with thatpolicy

This asks for a working attack against infrastructure you don't own. I can help with the defensive side of the same problem.

try instead

Installation

npx shadcn@latest add "@assistant-ui/elements-guardrail-notice"

Usage

import { GuardrailNotice } from "@/components/elements/guardrail-notice";

<GuardrailNotice
  title="I can't help with that"
  explanation="This asks for a working attack against infrastructure you don't own."
  policy="policy"
  alternatives={alternatives}
/>

Props

title*stringThe refusal in one line, stated plainly rather than apologetically.
explanation*stringWhy, and where the line actually sits.
policy*stringShort label for the rule that applied.
alternatives*string[]The nearest things it can do. Pass an empty array to hide the section.
onPick(alternative: string) => voidCalled when an alternative is chosen.
classNamestringExtra classes merged onto the root.

Source

guardrail-notice.tsx
"use client";

import { ShieldIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { mono, paper } from "./surfaces";

export function GuardrailNotice({
  title,
  explanation,
  policy,
  alternatives,
  onPick,
  className,
}: {
  title: string;
  explanation: string;
  policy: string;
  alternatives: readonly string[];
  onPick?: (alternative: string) => void;
  className?: string;
}) {
  return (
    <div
      className={cn(
        paper,
        "flex w-full max-w-sm flex-col gap-3 rounded-[20px] p-4",
        className,
      )}
    >
      <div className="flex items-center gap-2.5">
        <span className="flex size-7 shrink-0 items-center justify-center rounded-lg bg-amber-500/12 text-amber-600 dark:text-amber-400">
          <ShieldIcon className="size-3.5" />
        </span>
        <span className="min-w-0 flex-1 truncate text-[13.5px] font-medium">
          {title}
        </span>
        <span className={cn(mono, "text-foreground/30 shrink-0")}>
          {policy}
        </span>
      </div>

      <p className="text-foreground/60 text-xs leading-relaxed">
        {explanation}
      </p>

      {alternatives.length > 0 && (
        <div className="flex flex-col gap-1.5">
          <span className={cn(mono, "text-foreground/30")}>try instead</span>
          {alternatives.map((alternative) => (
            <button
              key={alternative}
              type="button"
              onClick={() => onPick?.(alternative)}
              className="hover:bg-foreground/[0.04] text-foreground/70 hover:text-foreground/95 -mx-1.5 rounded-lg px-1.5 py-1 text-start text-[13px] transition-colors"
            >
              {alternative}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}