Provides hooks and utility functions to create a custom and reusable autogenerated table of content for a React project, works really well with markdown content as well.
- React table of content
- Generates a table of content based on the headings in a React component.
- Supports custom selectors for headings.
- Supports custom dependencies to watch for changes.
- Provides utility functions to add IDs to heading tags in HTML.
- Provides utility functions to convert strings to dash case.
- Provides utility functions to convert links in HTML to external links (open in new tab).
- Lightweight and easy to use.
- No dependencies.
npm install react-table-of-content
import React from "react";
import { useTableOfContent } from "react-table-of-content";
export const TableOfContent: React.FC<{}> = () => {
const { headingLinks, contentIsActive } = useTableOfContent();
// return (
// <>JSX</>
// )
};
You can create a reusable component that generates a table of content for your page using the hook. See the example below.
Reusable custom TableOfContent component
import React from "react";
import { useTableOfContent } from "react-table-of-content";
export const TableOfContent: React.FC<{}> = () => {
const { headingLinks, contentIsActive } = useTableOfContent({
selectors: "article h1, article h2, article h3",
});
if (!headingLinks || headingLinks.length < 1) return null;
return (
<div>
<h4 id="on-this-page">ON THIS PAGE</h4>
<ul className="mt-4 space-y-2">
{headingLinks.map((link) => {
const isActive = contentIsActive(link.id);
return (
<li key={link.id}>
<a
className={`${
isActive ? "text-gray-800 font-semibold" : "text-gray-500"
}
${["h3", "h4"].includes(link.tagName.toLowerCase()) && "pl-2"}
${["h5", "h6"].includes(link.tagName.toLowerCase()) && "pl-4"}
hover:text-gray-700 transition-colors duration-200
`}
href={`#${link.id}`}
>
{link.title}
</a>
</li>
);
})}
</ul>
</div>
);
};
You can then use the component in your page like so:
Example App component that has needs table of content
import React from "react";
import { TableOfContent } from "./TableOfContent";
export const App: React.FC = () => {
return (
<div className="flex">
<div className="p-10 w-64">
<div className="sticky top-8">
<div className="mb-2">
<img className="w-8 h-8" src="/favicon.png" alt="logo" />
</div>
<TableOfContent />
</div>
</div>
<div className="flex-1">
<Article />
</div>
</div>
);
};
Below is an example of an article component that has headings.
Simple Article component with headings
import React from "react";
const Article = () => {
const headingTags = ["h1", "h2", "h3", "h4", "h5", "h6"];
const dummyText =
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Laborum vero accusamus alias cumque numquam atque eius ullam nobis at! Necessitatibus, corporis earum? Quidem, corporis blanditiis sapiente veritatis saepe debitis expedita!.";
return (
<article>
<div className="container max-w-screen-xl">
{headingTags.map((tag, index) => {
const headingElement = createElement(
tag,
{ key: index, id: `${tag}-heading` },
`${tag.toUpperCase()} heading`
);
return (
<section key={index}>
{headingElement}
{/* Returns something like this: <h1 id={`${tag}-heading`}>{tag.toUpperCase()} heading</h1> */}
<p>{dummyText}</p>
</section>
);
})}
</div>
</article>
);
};
The useTableOfContent
hook generates a table of content for a React component based on the headings in the component.
Name | Type | Required | Default | Description |
---|---|---|---|---|
selectors |
string |
Optional | "h1, h2, h3" |
A string of comma-separated selectors to use to generate the table of content. |
deps |
unknown |
Optional | undefined |
An item to be used within the dependency array to watch for changes. |
An object with the following properties:
Name | Type | Description |
---|---|---|
headingLinks |
array |
It contains an array of objects that have the following properties:id (string): The ID of the heading, title (string): The text content of the heading, tagName (string): The tag name of the heading. |
contentIsActive |
function |
A function that takes an ID of the headingLink and returns a boolean indicating whether the content with that ID is active. |
The package also exports utility functions that can be used to generate the table of content.
The addIdToHeadingTags
function adds an ID to each heading tag in a string of HTML.
Accepts 1 argument of type string
containing the HTML to add IDs to heading tags.
A string
of HTML with IDs added to the heading tags.
import { addIdToHeadingTags } from "react-table-of-content";
const html = `
<h1>Heading 1</h1>
<p>Content 1</p>
<h2>Heading 2</h2>
<p>Content 2</p>
`;
const newHtml = addIdToHeadingTags(html);
console.log(newHtml);
// Output:
// <h1 id="heading-1">Heading 1</h1>
// <p>Content 1</p>
// <h2 id="heading-2">Heading 2</h2>
// <p>Content 2</p>
It's a function that converts a string to dash case.
Accepts 1 argument of type string
to be transformed to a dash cased format.
A string in dash case.
import { dashCase } from "react-table-of-content";
const str = "This is a string";
const newStr = dashCase(str);
console.log(newStr);
// Output:
// this-is-a-string
It's a function that converts all links in a string of HTML to external links.
Accepts 1 argument of type string
containing the HTML to convert links to external links.
A string of HTML with links converted to external links.
import { makeLinksExternal } from "react-table-of-content";
const html = `
<a href="/about">About</a>
<a href="/contact">Contact</a>
`;
const newHtml = makeLinksExternal(html);
console.log(newHtml);
// Output:
// <a href="/about" target="_blank" rel="noopener noreferrer">About</a>
// <a href="/contact" target="_blank" rel="noopener noreferrer">Contact</a>
It's a function that calculates the read time of a string of text.
Accepts 1 argument of type string
containing the text to calculate the read time.
An object
with minutes
property of type number
in minutes and a readTime
property describing the number in words e.g. "1 min read".
import { readTime } from "react-table-of-content";
const text =
"This is a string of text that will be used to calculate the read time.";
const time = readTime(text);
console.log(time);
// Output:
// { minutes: 1, readTime: "1 min read" }
Unit and integration tests are written using Jest and React Testing Library. To run the tests, use the command below:
npm run test
You can mock the package in your tests like so:
jest.mock("react-table-of-content", () => ({
addIdToHeadingTags: jest.fn((content) => content),
useTableOfContent: jest.fn(() => ({
headingLinks: [
{
id: "id",
title: "title",
tagName: "h1",
},
],
contentIsActive: jest.fn(() => true),
})),
makeLinksExternal: jest.fn((content) => content),
}));
MIT
Precious OSSAI