'Why does my TOC component add `{"-" + 1}` to its links every time I click on one of them?
I implemented a table-of-contents component to my site following this tutorial.
It works, but every time I click on one of the TOC's links, all the links (including the clicked one) in the TOC get a {"-" + 1} appended. So if I click a link, all those links go from #first-heading, #second-heading, etc. to #first-heading-1, #second-heading-1, etc. If I click one of the links again, they will all get #first-heading-2, #second-heading-2 etc., and so on. This behavior is of course problematic, as it breaks the links.
What's causing this? How do I fix it?
I noticed the tutorial uses the remark-slug plugin for the headings, while I use the gatsby-autolink-headers plugin. Can that be the source of the problem? I've not been able to test with the former, as I get an error when trying to install it.
EDIT: I've tried with both plugins. Same problem.
TableOfContents.js
import React from "react"
import Slugger from "github-slugger"
import { Link } from "gatsby"
const slugger = new Slugger()
export default ({ headings }) => (
  <div className="table-of-contents">
    <h3>On this page</h3>
    <ol>
      {headings
        .filter(heading => heading.depth !== 1)
        .map(heading => (
          <li key={heading.value}>
            <Link
              to={"#" + slugger.slug(heading.value)}
            >
              {heading.value}
            </Link>
          </li>
        ))}
    </ol>
  </div>
)
post-template.js
import * as React from "react"
import { graphql } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import Layout from "../components/layout.js"
import Seo from "../components/seo.js"
const PostTemplate = ({ data, location }) => {
  let post = data.mdx
  return (
    <Layout location={location}>
      <Seo
        title={post.frontmatter.title}
        description={post.frontmatter.lead}
        date={post.frontmatter.computerDate}
      />
      <article className="article">
        <h1 itemprop="headline">{post.frontmatter.title}</h1>
        <p
          className="lead"
          itemprop="introduction"
        >
          {post.frontmatter.lead}
        </p>
        <MDXRenderer headings={post.headings}>
          {post.body}
        </MDXRenderer>
      </article>
    </Layout>
  )
}
export default PostTemplate
export const pageQuery = graphql`
  query PostBySlug($id: String!) {
    site {
      siteMetadata {
        title
      }
    }
    mdx(id: {eq: $id}) {
      id
      excerpt(pruneLength: 160)
      body
      frontmatter {
        title
        computerDate: date(formatString: "YYYY-MM-DD")
        humanDate: date(formatString: "DD MMMM YYYY")
        lead
      }
      headings {
        depth
        value
      }
    }
  }
`
index.mdx
---
/* frontmatter */
---
<!-- component imported as shortcode in `layout.js` -->
<TableOfContents headings={props.headings} />
layout.js (excerpt)
import TableOfContents from "./article-components/TableOfContents"
const shortcodes = {
  TableOfContents
}
export default function Layout({ children }) {
  return (
    <div className="layout-wrapper">
      <Header />
      <main>
        <MDXProvider components={shortcodes}>
          {children}
        </MDXProvider>
      </main>
    </div>
  )
}
Solution 1:[1]
It's because of the slugger. In their docs:
slugger.slug('foo') // returns 'foo' slugger.slug('foo') // returns 'foo-1' slugger.slug('bar') // returns 'bar' slugger.slug('foo') // returns 'foo-2'
Because it ensures that the links are unique (like GitHub does), it appends the -1, -2, etc.
As long as you use you gatsby-autolink-headers plugin can get rid of the slugger implementation. If you need, you can use the normal link value (heading.value), the slug field (if provided), or sanitize it using a custom function like:
function slugify (text) {
  return text
    .toString()
    .toLowerCase()
    .normalize(`NFD`)
    .trim()
    .replace(/\s+/g, `-`)
    .replace(/[^\w-]+/g, ``)
    .replace(/--+/g, `-`);
};
<Link to={"#" + slugify(heading.value)}>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source | 
|---|---|
| Solution 1 | Ferran Buireu | 
