惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 热门话题
T
The Blog of Author Tim Ferriss
IT之家
IT之家
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
N
Netflix TechBlog - Medium
D
Docker
Engineering at Meta
Engineering at Meta
阮一峰的网络日志
阮一峰的网络日志
Recent Announcements
Recent Announcements
雷峰网
雷峰网
博客园 - 司徒正美
大猫的无限游戏
大猫的无限游戏
美团技术团队
C
Cisco Blogs
V2EX - 技术
V2EX - 技术
N
News and Events Feed by Topic
Latest news
Latest news
博客园 - 三生石上(FineUI控件)
博客园 - Franky
Attack and Defense Labs
Attack and Defense Labs
C
CERT Recently Published Vulnerability Notes
S
Secure Thoughts
博客园_首页
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Microsoft Security Blog
Microsoft Security Blog
The GitHub Blog
The GitHub Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
V
V2EX
Hugging Face - Blog
Hugging Face - Blog
W
WeLiveSecurity
The Register - Security
The Register - Security
T
Tenable Blog
J
Java Code Geeks
The Cloudflare Blog
有赞技术团队
有赞技术团队
博客园 - 聂微东
P
Palo Alto Networks Blog
Security Latest
Security Latest
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
S
SegmentFault 最新的问题
H
Hacker News: Front Page
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
酷 壳 – CoolShell
酷 壳 – CoolShell
T
The Exploit Database - CXSecurity.com
S
Security @ Cisco Blogs
Cisco Talos Blog
Cisco Talos Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Hacker News: Ask HN
Hacker News: Ask HN

Streamdown Documentation

Configuration FAQ Getting Started Introduction Security Animation Carets Code Blocks Components GitHub Flavored Markdown Interactivity Internationalization Memoization Migrate from react-markdown Styling Unterminated Block Parsing Typography Usage @streamdown/cjk @streamdown/code Built-in Plugins @streamdown/math @streamdown/mermaid Custom renderers
Link Safety
Vercel · 2026-04-11 · via Streamdown Documentation

Configurable confirmation modal for external links to protect users from malicious URLs.

When rendering AI-generated or user-generated content, links can pose security risks. The link safety feature adds a confirmation modal before opening external links, similar to ChatGPT's implementation.

Link safety is enabled by default. When a user clicks any link, a confirmation modal appears with:

  • The full URL being opened
  • A "Copy link" button
  • An "Open link" button
  • Close via backdrop click or Escape key

To disable the confirmation modal and allow links to open directly:

import { Streamdown } from 'streamdown';

export default function Chat({ content }) {
  return (
    <Streamdown linkSafety={{ enabled: false }}>
      {content}
    </Streamdown>
  );
}

Use the onLinkCheck callback to allow trusted domains without showing the modal:

<Streamdown
  linkSafety={{
    enabled: true,
    onLinkCheck: (url) => {
      // Return true to allow without modal (safelist)
      // Return false to show confirmation modal
      return url.startsWith('https://your-app.com') ||
             url.startsWith('https://github.com');
    }
  }}
>
  {content}
</Streamdown>

The callback receives the URL and can return:

  • true - Open the link directly without modal
  • false - Show the confirmation modal
  • Promise<boolean> - Async checks are supported

Async Safelist Check

For server-side safelist validation:

<Streamdown
  linkSafety={{
    enabled: true,
    onLinkCheck: async (url) => {
      const response = await fetch('/api/check-url', {
        method: 'POST',
        body: JSON.stringify({ url }),
      });
      const { isSafe } = await response.json();
      return isSafe;
    }
  }}
>
  {content}
</Streamdown>

Replace the default modal with your own component using renderModal:

import { Streamdown, type LinkSafetyModalProps } from 'streamdown';

function CustomLinkModal({ url, isOpen, onClose, onConfirm }: LinkSafetyModalProps) {
  if (!isOpen) return null;

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <h2>External Link</h2>
        <p>You're about to visit:</p>
        <code>{url}</code>
        <div className="actions">
          <button onClick={onClose}>Cancel</button>
          <button onClick={onConfirm}>Continue</button>
        </div>
      </div>
    </div>
  );
}

export default function Chat({ content }) {
  return (
    <Streamdown
      linkSafety={{
        enabled: true,
        renderModal: (props) => <CustomLinkModal {...props} />,
      }}
    >
      {content}
    </Streamdown>
  );
}

The renderModal function receives:

PropTypeDescription
urlstringThe URL being opened
isOpenbooleanWhether the modal is visible
onClose() => voidCall to close the modal
onConfirm() => voidCall to open the link and close the modal

Link safety works alongside content hardening. Use both for comprehensive protection:

import { Streamdown, defaultRehypePlugins } from 'streamdown';
import { harden } from 'rehype-harden';

export default function SecureChat({ content }) {
  return (
    <Streamdown
      linkSafety={{
        enabled: true,
        onLinkCheck: (url) => url.startsWith('https://trusted.com'),
      }}
      rehypePlugins={[
        defaultRehypePlugins.raw,
        [
          harden,
          {
            allowedLinkPrefixes: [
              'https://trusted.com',
              'https://github.com',
            ],
            allowedProtocols: ['https', 'mailto'],
          },
        ],
      ]}
    >
      {content}
    </Streamdown>
  );
}

This provides two layers of protection:

  1. Content hardening - Blocks or rewrites disallowed URLs at render time
  2. Link safety modal - Requires user confirmation before navigation

Import the types for custom modal implementations:

import type { LinkSafetyConfig, LinkSafetyModalProps } from 'streamdown';

const config: LinkSafetyConfig = {
  enabled: true,
  onLinkCheck: (url) => url.startsWith('https://safe.com'),
  renderModal: (props: LinkSafetyModalProps) => <CustomModal {...props} />,
};

LinkSafetyConfig

PropertyTypeDefaultDescription
enabledbooleantrueEnable link interception and modal
onLinkCheck(url: string) => boolean | Promise<boolean>-Optional safelist callback
renderModal(props: LinkSafetyModalProps) => ReactNode-Optional custom modal component

LinkSafetyModalProps

PropertyTypeDescription
urlstringThe URL to be opened
isOpenbooleanModal visibility state
onClose() => voidClose the modal without navigation
onConfirm() => voidConfirm and open the link