Esta página se muestra en inglés. Una traducción al español está en curso.
SkillsMay 11, 2026·4 min de lectura

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.

Listo para agents

Instalación lista para agent

Este activo puede instalarse después de elegir el runtime, revisar el plan y ejecutar el comando correspondiente.

Native · 98/100Política: permitir
Superficie agent
Cualquier agent MCP/CLI
Tipo
Skill
Instalación
Single
Confianza
Confianza: Community
Entrada
Asset
Comando de instalación directa
npx -y tokrepo@latest install dd02260f-0044-4e5a-8e2d-5849c95fd587 --target codex

Ejecutar después de confirmar el plan con dry-run.

Introducción

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+

🙏

Fuente y agradecimientos

Pattern compiled from Perplexity API docs + react-markdown.

remarkjs/react-markdown — ⭐ 13,000+

Discusión

Inicia sesión para unirte a la discusión.
Aún no hay comentarios. Sé el primero en compartir tus ideas.

Activos relacionados