Cette page est affichée en anglais. Une traduction française est en cours.
SkillsMay 11, 2026·4 min de lecture

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.

Prêt pour agents

Cet actif peut être lu et installé directement par les agents

TokRepo expose une commande CLI universelle, un contrat d'installation, le metadata JSON, un plan selon l'adaptateur et le contenu raw pour aider les agents à juger l'adaptation, le risque et les prochaines actions.

Native · 98/100Policy : autoriser
Surface agent
Tout agent MCP/CLI
Type
Skill
Installation
Single
Confiance
Confiance : New
Point d'entrée
Asset
Commande CLI universelle
npx tokrepo install dd02260f-0044-4e5a-8e2d-5849c95fd587
Introduction

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

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 `<sup><a href="${url}" target="_blank" rel="noopener noreferrer" title="${url}">[${n}]</a></sup>`;
  });

  return (
    <div className="prose">
      <ReactMarkdown
        components={{ a: ({ node, ...props }) => <a {...props} className="text-blue-600 hover:underline" /> }}
        rehypePlugins={[]}
        skipHtml={false}
      >
        {withFootnotes}
      </ReactMarkdown>

      <h3 className="mt-6 text-sm font-semibold">Sources</h3>
      <ol className="text-sm">
        {citations.map((url, i) => (
          <li key={i}>
            <span className="text-gray-500">[{i + 1}]</span>
            <a href={url} target="_blank" rel="noopener noreferrer" className="ml-2">{new URL(url).hostname}</a>
          </li>
        ))}
      </ol>
    </div>
  );
}

Hover preview tooltip (optional)

import { Tooltip } from "@radix-ui/react-tooltip";

function CitationSup({ n, url }: { n: number; url: string }) {
  return (
    <Tooltip>
      <Tooltip.Trigger asChild>
        <sup>
          <a href={url} target="_blank" rel="noopener noreferrer">[{n}]</a>
        </sup>
      </Tooltip.Trigger>
      <Tooltip.Content className="bg-black text-white px-2 py-1 rounded text-xs">
        {new URL(url).hostname} — click to open
      </Tooltip.Content>
    </Tooltip>
  );
}

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.


Quick Use

  1. Get content and citations[] from Sonar response
  2. Regex [\d+] markers in content, replace with <sup><a> 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

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 `<sup><a href="${url}" target="_blank" rel="noopener noreferrer" title="${url}">[${n}]</a></sup>`;
  });

  return (
    <div className="prose">
      <ReactMarkdown
        components={{ a: ({ node, ...props }) => <a {...props} className="text-blue-600 hover:underline" /> }}
        rehypePlugins={[]}
        skipHtml={false}
      >
        {withFootnotes}
      </ReactMarkdown>

      <h3 className="mt-6 text-sm font-semibold">Sources</h3>
      <ol className="text-sm">
        {citations.map((url, i) => (
          <li key={i}>
            <span className="text-gray-500">[{i + 1}]</span>
            <a href={url} target="_blank" rel="noopener noreferrer" className="ml-2">{new URL(url).hostname}</a>
          </li>
        ))}
      </ol>
    </div>
  );
}

Hover preview tooltip (optional)

import { Tooltip } from "@radix-ui/react-tooltip";

function CitationSup({ n, url }: { n: number; url: string }) {
  return (
    <Tooltip>
      <Tooltip.Trigger asChild>
        <sup>
          <a href={url} target="_blank" rel="noopener noreferrer">[{n}]</a>
        </sup>
      </Tooltip.Trigger>
      <Tooltip.Content className="bg-black text-white px-2 py-1 rounded text-xs">
        {new URL(url).hostname} — click to open
      </Tooltip.Content>
    </Tooltip>
  );
}

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 + react-markdown.

remarkjs/react-markdown — ⭐ 13,000+

🙏

Source et remerciements

Pattern compiled from Perplexity API docs + react-markdown.

remarkjs/react-markdown — ⭐ 13,000+

Fil de discussion

Connectez-vous pour rejoindre la discussion.
Aucun commentaire pour l'instant. Soyez le premier à partager votre avis.

Actifs similaires