All articles

shadcn/ui Components: Where User-Controlled HTML Sneaks In

shadcn/ui Components: Where User-Controlled HTML Sneaks In

shadcn/ui is copy-paste components, which means you own them — and any sanitization they miss. Here are the three props that vibe-coded apps pass user content to and regret.

April 25, 2026VibeWShield Team1 min read

shadcn/ui gives you control, but control means you're responsible for every render path. Three props in the default shadcn library are reliably user-controlled in vibe-coded apps and reliably un-sanitized.

1. <Toast description={...}>

The default Toast variants render description as a React child. If you pass dangerouslySetInnerHTML or unescaped error messages from your API, they render.

toast({ description: error.message });  // "error.message" came from user input

Fix: strip HTML before rendering.

2. <AlertDialogDescription>

Same story. The component is a <p> tag with children. React's JSX auto-escaping saves you for text — until someone passes an element:

<AlertDialogDescription>
  {response.message /* "Hello <script>alert(1)</script>" */}
</AlertDialogDescription>

If message is a JSX node (server-sent), it renders. Text strings are safe; nested components aren't.

3. <Badge variant="destructive">{dynamic}</Badge>

Looks innocuous. Becomes bad when {dynamic} is <img src=x onerror=alert(1) />. React escapes text; it doesn't escape elements.

The fix

  • Strip HTML with sanitize-html or DOMPurify before you pass anything to a child prop.
  • Treat API responses like untrusted — never render a message field from an error as JSX.

VibeWShield's XSS scanner enumerates form fields and replays them with payloads. Anything that round-trips into the DOM un-escaped is a reflected XSS finding.

Free security scan

Test your app for these vulnerabilities

VibeWShield automatically scans for everything covered in this article and more — 18 security checks in under 3 minutes.

Scan your app free