Step 1/2
project details

Tell us about
the project

Tell us everything! Share your thoughts, ideas, and any specifics about your project. The more details, the better.

Select services
selection
Clear selection
Contact Details

How can we reach you?

Final step! Fill out your contact info, and we’ll be in touch to discuss your project soon.

Dropdown
Thank you for contacting us!

We will be in touch within 48 working hours

Keep your eyes peeled for an email from one of our founders.

Okay, got it!
Oops! Something went wrong while submitting the form.

Copy Link to Heading in Webflow Rich Text

Give every H2 and H3 in your rich text its own shareable link, with a one-click copy button.

Javascript

Copy code icon

Copy Code

<!-- Heading copy links: styles -->
<style>
.w-richtext h2,
.w-richtext h3 { scroll-margin-top: 1.5rem; }
.heading-anchor {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin-left: .5rem;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  opacity: 0;
  transition: opacity .2s;
}
.heading-anchor svg { display: block; }
.w-richtext h2:hover .heading-anchor,
.w-richtext h3:hover .heading-anchor,
.heading-anchor:focus-visible,
.heading-anchor.is-copied { opacity: 1; }
.heading-anchor .icon-check { display: none; }
.heading-anchor.is-copied .icon-link { display: none; }
.heading-anchor.is-copied .icon-check { display: block; }
.heading-anchor.is-copied { color: #1f9d55; }
@media (hover: none) { .heading-anchor { opacity: 1; } }
</style>

<!-- Heading copy links: script -->
<script>
(() => {
  const used = {};
  document.querySelectorAll('.w-richtext').forEach(rt => {
    rt.querySelectorAll('h2, h3').forEach(h => {
      let slug = h.id || h.textContent.trim().toLowerCase()
        .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
        .replace(/[^\w\s-]/g, '').replace(/\s+/g, '-');
      if (!slug) return;
      if (used[slug]) slug += '-' + used[slug]++; else used[slug] = 1;
      h.id = slug;

      const btn = document.createElement('button');
      btn.type = 'button';
      btn.className = 'heading-anchor';
      btn.setAttribute('aria-label', 'Copy link to this section');
      btn.innerHTML = '<svg class="icon-link" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg><svg class="icon-check" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>';

      btn.addEventListener('click', () => {
        const url = location.origin + location.pathname + '#' + slug;
        navigator.clipboard?.writeText(url).catch(() => {});
        history.replaceState(null, '', '#' + slug);
        btn.classList.add('is-copied');
        btn.setAttribute('aria-label', 'Link copied');
        setTimeout(() => {
          btn.classList.remove('is-copied');
          btn.setAttribute('aria-label', 'Copy link to this section');
        }, 1500);
      });

      h.appendChild(btn);
    });
  });
})();
</script>

What is the context here?

Long blog posts and guides are hard to share. Someone wants to send a colleague to one section, so they send the whole page and say "scroll down to the bit about pricing".

This snippet gives every H2 and H3 inside a rich text element its own ID and adds a small link icon next to it. Click the icon and the URL for that section is copied to the clipboard.

How to set it up

  • Paste the full snippet into the Before </body> tag custom code on your CMS template page. Use the site settings instead if you want it on every page.
  • You don't need to create any classes in Webflow. The script targets Webflow's default rich text class (.w-richtext) and adds a .heading-anchor class to each button it creates, which the CSS styles. If .heading-anchor clashes with a class already on your site, rename it in both the style and script tags.
  • Change the tick colour by swapping #1f9d55 for your brand colour or a variable.
  • If you have a fixed navbar, set scroll-margin-top to your nav height. Otherwise the heading lands underneath the nav.
  • Publish and test on the live site. Custom code doesn't run in the Designer.

How it works

  • Finds every H2 and H3 inside the rich text elements on the page.
  • Builds an ID from the heading text, so "How it works" becomes #how-it-works. Duplicate headings get -1, -2 added, and headings that already have an ID keep it.
  • Adds a link button to each heading. It shows on hover and on keyboard focus, and it's always visible on touch devices.
  • On click, it copies the full URL including the section link, updates the address bar, and shows a tick for 1.5 seconds.

Got a Crazy Idea? We're All Ears

Reach out, share your story, and Isaac or Tom will get back to you, usually same day. Let’s make some magic together.

Four men posing for a photo in a room with concrete walls and floor, three seated on a blue couch and one standing behind. From left to right on the couch are Jake in a blue sweater and jeans, Te Manihera in an orange t-shirt and dark pants, and Isaac in a black t-shirt with tattoos on his right forearm wearing brown shoes. Standing behind the couch is Tom, smiling broadly in a light-patterned shirt. Each man has his name written above with an arrow pointing to him.