# Perplexity Citations — Render Source Footnotes in Your UI > Parse Perplexity inline citation markers ([1][2][3]) + the citations URL array into clickable footnote UI. Markdown render, hover preview. ## Install Save the content below to `.claude/skills/` or append to your `CLAUDE.md`: ## Quick Use 1. Get `content` and `citations[]` from Sonar response 2. Regex `[\d+]` markers in content, replace with `` linking to citations[i-1] 3. Render with react-markdown allowing inline HTML, append a Sources list --- ## Intro Perplexity Sonar returns inline citation markers like `[1]`, `[2]` embedded in the markdown answer, plus a separate `citations` URL array. To turn this into a polished UI you need to: parse the markers, attach them to URLs by index, render as clickable superscript footnotes, and ideally show a preview tooltip. This skill is the React + Markdown pattern that does all four. Best for: any product that surfaces Sonar answers in a UI. Works with: react-markdown, remark/rehype, any Markdown renderer. Setup time: 15 minutes. --- ### React component ```tsx import ReactMarkdown from "react-markdown"; interface Props { content: string; citations: string[] } export function SonarAnswer({ content, citations }: Props) { // Replace [1] [2] etc with HTML superscript anchors const withFootnotes = content.replace(/\[(\d+)\]/g, (_, n) => { const idx = parseInt(n) - 1; const url = citations[idx]; if (!url) return `[${n}]`; return `[${n}]`; }); return (
}} rehypePlugins={[]} skipHtml={false} > {withFootnotes}

Sources

    {citations.map((url, i) => (
  1. [{i + 1}] {new URL(url).hostname}
  2. ))}
); } ``` ### Hover preview tooltip (optional) ```tsx import { Tooltip } from "@radix-ui/react-tooltip"; function CitationSup({ n, url }: { n: number; url: string }) { return ( [{n}] {new URL(url).hostname} — click to open ); } ``` ### Link safety checklist - `target="_blank"` + `rel="noopener noreferrer"` — prevents reverse tabnabbing - Sanitize URLs — reject `javascript:`, `data:`, anything not http(s) - Don't auto-fetch the source server-side without consent — privacy - Log click-throughs separately to attribute traffic to Sonar in analytics ### Edge cases to handle | Case | Handling | |---|---| | Marker `[42]` but only 5 citations | Render as plain `[42]` (no link) | | Same citation cited 3× | Same number; deduplicate in Sources list | | Citation URL with tracking params | Strip `utm_*` before display | | Non-HTTP URL | Drop or display as plain text | --- ### FAQ **Q: What if a marker has no matching citation?** A: Render the raw marker without a link. Don't error out — sometimes Sonar refers to inline context the user knows. Logging the mismatch is useful for future debugging. **Q: Should I prefetch source pages?** A: No — privacy + cost issues. Let users click through. If you want richer previews, use the source URL's `og:image` and `og:description` via a separate metadata fetch with cache. **Q: Can I get structured citations server-side?** A: Sonar Pro returns `search_results` array alongside `citations` with title + URL + last_updated. That's the structured form for richer rendering. --- ## Source & Thanks > Pattern compiled from [Perplexity API docs](https://docs.perplexity.ai) + react-markdown. > > [remarkjs/react-markdown](https://github.com/remarkjs/react-markdown) — ⭐ 13,000+ --- ## 快速使用 1. 从 Sonar 响应拿 `content` 和 `citations[]` 2. content 里正则 `[\d+]` 标记,替换为指向 citations[i-1] 的 `` 3. 用允许内联 HTML 的 react-markdown 渲染,附加 Sources 列表 --- ## 简介 Perplexity Sonar 在 markdown 答案里嵌内联引用标记如 `[1]`、`[2]`,加上一个单独的 `citations` URL 数组。要把这个变成精致 UI 需要:解析标记、按索引挂到 URL、渲染成可点上标脚注、最好还有 hover 预览。这个 skill 是 React + Markdown 模式做完四件事。适合任何在 UI 里展示 Sonar 答案的产品。兼容 react-markdown、remark/rehype、任何 Markdown 渲染器。装机时间 15 分钟。 --- ### React 组件 ```tsx import ReactMarkdown from "react-markdown"; interface Props { content: string; citations: string[] } export function SonarAnswer({ content, citations }: Props) { // 把 [1] [2] 等替换为 HTML 上标锚点 const withFootnotes = content.replace(/\[(\d+)\]/g, (_, n) => { const idx = parseInt(n) - 1; const url = citations[idx]; if (!url) return `[${n}]`; return `[${n}]`; }); return (
}} rehypePlugins={[]} skipHtml={false} > {withFootnotes}

来源

    {citations.map((url, i) => (
  1. [{i + 1}] {new URL(url).hostname}
  2. ))}
); } ``` ### Hover 预览 tooltip(可选) ```tsx import { Tooltip } from "@radix-ui/react-tooltip"; function CitationSup({ n, url }: { n: number; url: string }) { return ( [{n}] {new URL(url).hostname} —— 点开 ); } ``` ### 链接安全清单 - `target="_blank"` + `rel="noopener noreferrer"` —— 防反向 tabnabbing - Sanitize URL —— 拒绝 `javascript:` / `data:` / 任何非 http(s) - 未经同意别服务端自动抓源 —— 隐私 - 单独记点击,让分析能把流量归因到 Sonar ### 要处理的边缘情况 | 情况 | 处理 | |---|---| | 标记 `[42]` 但只有 5 个引用 | 渲染成纯 `[42]`(不带链接)| | 同一引用引了 3 次 | 同一编号;Sources 列表去重 | | 引用 URL 带追踪参数 | 显示前剥 `utm_*` | | 非 HTTP URL | 丢弃或显纯文本 | --- ### FAQ **Q: 标记没对应引用怎办?** A: 渲染裸标记不带链接。别报错 —— 有时 Sonar 指的是用户已知的内联上下文。记录不匹配便于以后调试。 **Q: 要预拉源页面吗?** A: 别 —— 隐私 + 成本问题。让用户点过去。要更丰富预览,单独抓源 URL 的 `og:image` + `og:description` 加缓存。 **Q: 能服务端拿结构化引用吗?** A: Sonar Pro 在 `citations` 之外返回 `search_results` 数组,含 title + URL + last_updated。这就是结构化形式,方便更丰富渲染。 --- ## 来源与感谢 > Pattern compiled from [Perplexity API docs](https://docs.perplexity.ai) + react-markdown. > > [remarkjs/react-markdown](https://github.com/remarkjs/react-markdown) — ⭐ 13,000+ --- Source: https://tokrepo.com/en/workflows/perplexity-citations-render-source-footnotes-in-your-ui Author: Perplexity