diff --git a/apify-docs-theme/src/theme/Heading/index.jsx b/apify-docs-theme/src/theme/Heading/index.jsx
new file mode 100644
index 000000000..bed905c13
--- /dev/null
+++ b/apify-docs-theme/src/theme/Heading/index.jsx
@@ -0,0 +1,85 @@
+import { useThemeConfig } from '@docusaurus/theme-common';
+import { translate } from '@docusaurus/Translate';
+import useBrokenLinks from '@docusaurus/useBrokenLinks';
+import clsx from 'clsx';
+import React, { useEffect } from 'react';
+
+import { LinkIcon } from '@apify/ui-icons';
+import { useCopyToClipboard } from '@apify/ui-library';
+
+import styles from './styles.module.css';
+
+export default function Heading({ as: As, id, ...props }) {
+ const brokenLinks = useBrokenLinks();
+ const {
+ navbar: { hideOnScroll },
+ } = useThemeConfig();
+
+ const { isCopied, copyToClipboard } = useCopyToClipboard();
+
+ // Register the anchor ID so Docusaurus can scroll to it
+ useEffect(() => {
+ if (id) {
+ brokenLinks.collectAnchor(id);
+
+ // Handle scroll on page load if this heading matches the URL hash
+ const hash = decodeURIComponent(window.location.hash.slice(1));
+ if (hash === id) {
+ // Use setTimeout to ensure the page is fully rendered
+ setTimeout(() => {
+ const element = document.getElementById(id);
+ if (element) {
+ element.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }
+ }, 100);
+ }
+ }
+ }, [id, brokenLinks]);
+
+ // H1 headings and headings without an id shouldn't have the copy to clipboard button
+ if (As === 'h1' || !id) {
+ return ;
+ }
+
+ const handleCopy = async (e) => {
+ e.preventDefault();
+ const url = new URL(window.location.href);
+ url.hash = `#${id ?? ''}`;
+ window.location.hash = `#${id ?? ''}`;
+ await copyToClipboard(url.toString());
+ };
+
+ const anchorTitle = translate(
+ {
+ id: 'theme.common.headingLinkTitle',
+ message: 'Direct link to {heading}',
+ description: 'Title for link to heading',
+ },
+ {
+ heading: typeof props.children === 'string' ? props.children : id,
+ },
+ );
+
+ return (
+
+ {props.children}
+
+
+
+
+ );
+}
diff --git a/apify-docs-theme/src/theme/Heading/styles.module.css b/apify-docs-theme/src/theme/Heading/styles.module.css
new file mode 100644
index 000000000..a94765a22
--- /dev/null
+++ b/apify-docs-theme/src/theme/Heading/styles.module.css
@@ -0,0 +1,47 @@
+.anchorWithStickyNavbar {
+ scroll-margin-top: calc(var(--ifm-navbar-height) + 0.5rem);
+}
+
+.anchorWithHideOnScrollNavbar {
+ scroll-margin-top: 0.5rem;
+}
+
+.headingCopyIcon {
+ display: none;
+ position: relative;
+ left: .4rem;
+ color: var(--ifm-color-emphasis-700);
+ text-decoration: none;
+}
+
+.headingCopyIcon svg {
+ stroke: var(--ifm-color-primary);
+ max-height: 1em !important;
+}
+
+h2:hover .headingCopyIcon,
+h3:hover .headingCopyIcon,
+h4:hover .headingCopyIcon,
+h5:hover .headingCopyIcon,
+h6:hover .headingCopyIcon {
+ display: inline-block;
+}
+
+.headingCopyIcon.copied {
+ display: inline-block !important;
+}
+
+.headingCopyIcon.copied svg {
+ display: none;
+}
+
+.headingCopyIcon.copied::after {
+ content: 'Copied!';
+ font-size: 12px;
+ color: var(--ifm-font-color-secondary);
+ font-weight: 600;
+ margin-left: 8px;
+ vertical-align: middle;
+ line-height: 1;
+}
+
diff --git a/package-lock.json b/package-lock.json
index eb230c89b..e61c2c828 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -63,7 +63,7 @@
},
"apify-docs-theme": {
"name": "@apify/docs-theme",
- "version": "1.0.214",
+ "version": "1.0.216",
"license": "ISC",
"dependencies": {
"@apify/docs-search-modal": "^1.3.3",