# Your articles need a “Copy for AI” button

> Source: <https://markodenic.tech/your-articles-need-a-copy-for-ai-button/>
> Published: 2026-10-17 10:00:14+00:00

# Your articles need a “Copy for AI” button

**Creem.io**

Merchant of record payments for SaaS and digital products, handling checkout, subscriptions, taxes, invoices, and global payment operations.

[Sponsor this newsletter · reach 9,500+ active developers](https://markodenic.tech/sponsorship/)

Your readers already paste your articles into AI chats. They want a summary, a version of your code for their stack, or an answer to the question your article almost covered. So they select the page, copy, paste.

What the AI gets is a mess: menu links, “Copy” button labels, line numbers glued to the code, and prose and code mashed together with no clear edges. Docs sites have started fixing this with a “Copy for AI” button that hands over a clean Markdown version of the page instead. You can add one in about 30 lines.

## The fix

Walk the article and turn each element into its Markdown equivalent:

```
function toMarkdown(node) {
  if (node.nodeType === Node.TEXT_NODE) {
    return node.parentNode.closest('p, li, h1, h2, h3') ? node.textContent.replace(/\s+/g, ' ') : '';
  }
  if (node.nodeType !== Node.ELEMENT_NODE || node.matches('button, nav, script, style, [data-no-copy]')) return '';

  const inner = [...node.childNodes].map(toMarkdown).join('');
  const text = inner.trim();

  switch (node.tagName) {
    case 'H1': return `# ${text}\n\n`;
    case 'H2': return `## ${text}\n\n`;
    case 'H3': return `### ${text}\n\n`;
    case 'P': return `${text}\n\n`;
    case 'UL': case 'OL': return `${inner}\n`;
    case 'LI': return `- ${text}\n`;
    case 'STRONG': return `**${text}**`;
    case 'EM': return `_${text}_`;
    case 'A': return `[${text}](${node.href})`;
    case 'CODE': return `\`${node.textContent}\``;
    case 'PRE': {
      const lang = node.querySelector('code')?.className.match(/language-(\w+)/)?.[1] ?? '';
      return `\`\`\`${lang}\n${node.textContent.trim()}\n\`\`\`\n\n`;
    }
    default: return inner;
  }
}
```

Then copy it on click, with the page URL on top so the AI can point back to the source:

``` js
copyButton.addEventListener('click', async () => {
  const markdown = toMarkdown(document.querySelector('article')).trim();
  await navigator.clipboard.writeText(`Source: ${location.href}\n\n${markdown}`);
  copyButton.textContent = 'Copied!';
});
```

This post has the button. Click “Copy for AI” next to the date at the top, paste it into any AI chat or text editor, and compare it with a plain select-and-copy of the same page.

## What the converter handles

- **Headings, paragraphs, lists:** become`#` , plain text, and`-` items.
- **Links:** keep their full URL, because`node.href` is always absolute. Relative links would be useless outside your site.
- **Code blocks:** become fenced blocks, and the`language-js` class most syntax highlighters add becomes the fence’s language. Inline`code` keeps its backticks.
- **Noise:** buttons, navigation, scripts and styles are skipped. Anything else you want left out, like a newsletter signup box in the middle of the article, gets a`data-no-copy` attribute.
- **Whitespace:** text outside paragraphs, list items and headings is dropped. That is almost always the indentation of your HTML, which would otherwise end up as stray spaces.

It is not a full HTML-to-Markdown converter. Tables, images and nested lists are not covered. Add a `case` when your content needs one: it is one line each.

## Why not just give them the URL?

Some AI tools can open a link. Many cannot, some get blocked by the site, and some read a cached copy from months ago. Pasted text always works, and it is exactly what the reader is looking at right now.

Markdown is also the format models handle best. A heading is one `#` instead of a wrapper of classes, and code fences mark exactly where code starts and ends, so the model stops mixing your explanation into your code.

## Where this comes up

- **Tutorials and blog posts:** “rewrite this for my framework” is the most common thing readers do with them.
- **Documentation:** developers paste docs pages into their coding assistant to get help on your API.
- **Changelogs and release notes:** “what changed that affects me?”
- **Long policies and guides:** support articles, terms, onboarding checklists.

## Why this matters

- **Readers are already doing it:** the button only decides whether the AI gets your content clean or garbled.
- **Better answers about your content:** clean structure means fewer wrong explanations of your code.
- **It works everywhere:** the Clipboard API is supported in every modern browser. It needs HTTPS and a click, which a button already gives you.

If you like where this is going, my issue on [letting AI agents use your site with 3 HTML attributes](https://markodenic.tech/let-ai-agents-use-your-site-with-3-html-attributes/) covers the other side: agents using your features, not just reading your content. And if you’d rather offer one button for every app, the [native share button](https://markodenic.tech/how-to-add-a-native-share-button-with-four-lines-of-javascript/) is four lines.

Put the button next to your article’s title and paste the result into an AI chat. You will see the difference in the first answer.

Happy coding!

Marko
