Lewati ke konten
Rama's logo Qisthi Ramadhani
Go back

Adding an LLM-Friendly 'Copy to Markdown' Feature to Astro Paper (Inspired by RSPress)

TL;DR: Transform your Astro Paper blog into an LLM-ready resource by adding dynamic /.md text endpoints and a split-button Copy to Markdown UI component. This approach (inspired by RSPress) guarantees 100% accuracy when readers transfer your blog’s content into AI interfaces like Claude and ChatGPT.

Astro LLM Features Banner

The shift from traditional SEO to GEO (Generative Engine Optimization) requires us, as developers, to adapt. Today, readers of technical blogs have a new habit: they no longer just stare at the screen; they copy tutorial contents, paste them into ChatGPT or Claude, and ask, “Can you adapt this script for my Ubuntu environment?”

Unfortunately, the default browser copy-paste behavior introduces significant technical friction.

The Conventional Copy-Paste Problem: HTML vs. Markdown

When we highlight text on a browser interface (HTML) to copy it, we aren’t just copying text. We often inadvertently copy messy DOM hierarchies, lose indentation spaces in code blocks, and accidentally include UI elements (breadcrumbs, footers) directly into the AI’s prompt box.

Prompt engineering research shows that this phenomenon burdens the model. The more noise or irrelevant elements present in the prompt, the higher the potential for LLM hallucinations. Markdown, on the other hand, is widely recognized as the “native language” that LLMs understand highly efficiently.

Comparison AspectCopy via HTML Selection (Bad Practice)Raw Markdown Extraction (Best Practice)
Token LoadMassive (HTML bloat, empty spaces)Highly efficient and lightweight
Logical HierarchyLost (relies strictly on CSS rendering)Extremely precise (#, ##, *)
Code IntegrityFrequently loses indentation (tabs/spaces)Identical to the original source code (```)
Contextual AccuracyProne to distraction by navigation text100% focused on the tutorial body content

The optimal solution is to emulate the innovation introduced by RSPress: providing a dedicated button that downloads the raw Markdown document and directly loads the pristine configuration into the user’s clipboard.

Implementing the llm.md Endpoint in Astro

We are currently using the Astro Paper template. The first step is to create a dynamic API endpoint in Astro that automatically returns the pure Markdown version of every post.

Create a new file at src/pages/blog/[...slug]/llm.md.ts:

import { getCollection } from "astro:content";
import fs from "fs";
import path from "path";
import { BLOG_PATH } from "@/content.config";

export async function getStaticPaths() {
  const posts = await getCollection("blog", ({ data }) => !data.draft);
  return posts.map(post => ({
    params: { slug: post.id },
    props: { post },
  }));
}

export async function GET({ props }) {
  const { post } = props;
  const filePath = path.join(process.cwd(), BLOG_PATH, `${post.id}.md`);
  const fileContent = fs.readFileSync(filePath, "utf-8");

  // Regex to strip YAML Frontmatter (cleaner for LLMs)
  const markdownContent = fileContent.replace(/^---[\s\S]+?---\n*/, "");

  const header = `> Technical blog by Qisthi Ramadhani\n\n# ${post.data.title}\n\n`;

  return new Response(header + markdownContent, {
    headers: {
      "Content-Type": "text/markdown; charset=utf-8",
      "Cache-Control": "public, max-age=3600",
    },
  });
}

The script above statically builds endpoints for all blog pages (thanks to getStaticPaths), strips away the unnecessary frontmatter using a Regex, adds simple metadata, and serves the response with a Content-Type: text/markdown header.

Building the UI: The LlmActions.astro Component

Once the endpoint is available, the next task is to create a sleek RSPress-style split-button UI using Tailwind CSS and a bit of Alpine-style inline Javascript.

Create src/components/LlmActions.astro and import it into your PostDetails.astro layout. Here is a simplified wireframe of the code:

---
interface Props {
  llmMdUrl: string; // example prop passed: /blog/slug/llm.md
}
const { llmMdUrl } = Astro.props;
---

<div class="flex flex-col items-center gap-1">
  <span class="italic">LLM-friendly version:</span>
  <div class="flex gap-2">
    <!-- Button 1: Main Action (Copy to Clipboard) -->
    <button id="llm-copy-md" data-llm-url={llmMdUrl}>
       <span>Copy Markdown</span>
    </button>

    <!-- Button 2: Dropdown Trigger for ChatGPT & Claude -->
    <!-- (Use SVG and Tailwind Dropdown states here) -->
  </div>
</div>

<script is:inline>
  document.getElementById("llm-copy-md").addEventListener("click", async function() {
    const url = this.getAttribute("data-llm-url");
    const doc = await (await fetch(url)).text();
    await navigator.clipboard.writeText(doc); // Write to OS clipboard
    alert("Markdown copied for LLM!");
  });
</script>

Pro Tip: Leverage URLSearchParams to directly open AI assistants with prompt context. You can structure links like https://chatgpt.com/?hints=search&q=Read+URL+and+Summarize inside your dropdown menu options.

Conclusion

Transitioning from a purely visual era (HTML+CSS) towards providing machine-friendly contextual data (Markdown) marks the evolution of modern platforms. By adding the Copy to Markdown feature, an Astro-based blog can secure a top spot in the GEO ecosystem food chain, delivering extreme value for technical readers heavily reliant on AI assistants.


Share this post on:
LLM-friendly version:
Open in ChatGPT Open in Claude

Next Post
Deploying Laravel Reverb WebSocket Server with Nginx