A Log of Implementing Share Buttons in Docusaurus
I've added social media share buttons to each article on this site. This post documents the implementation process and the technical decisions behind it.
The completed component works as follows:
The main feature of this component is its ability to automatically fetch the page title and URL. This means that once it's set up, there's no need for special configuration for each article, making it low-maintenance.
Design Policy: Loose Coupling and Maintainability
Docusaurus has a powerful feature called swizzle
that allows you to directly override core theme components. However, I chose not to use this feature for this implementation.
The reason is that using swizzle
tightly couples the project to a specific Docusaurus version, increasing the risk of incompatibility and breaking changes during future upgrades.
Prioritizing the long-term stability and maintainability of the project, I chose to implement it as an independent React component that is less susceptible to updates of the Docusaurus core.
Implementation File Details
After installing the react-share
library, the implementation proceeded by creating two main files. The completed files can be viewed on GitHub.
src/components/ShareButtons/index.tsx
(View on GitHub)src/components/ShareButtons/styles.module.css
(View on GitHub)
Here are the details of each file.
1. Component Logic (index.tsx
)
This file manages all the functionality of the share buttons.
First, to address the type definition incompatibility between React 19 and the react-share
library, I intentionally type-casted the imported components with as any
. This safely bypasses type errors originating from the library.
// ... (imports) ...
// Safely bypass the issue of the library's type definitions not being compatible with React 19
const TwitterShareButton = OriginalTwitterShareButton as any;
const FacebookShareButton = OriginalFacebookShareButton as any;
const HatenaShareButton = OriginalHatenaShareButton as any;
Next, I use the useEffect
hook to dynamically fetch the current page's URL and title on the client side. By removing the site name from document.title
, I ensure that only the pure article title is shared.
useEffect(() => {
// Generate the full URL of the current page
setPageUrl(new URL(location.pathname, siteConfig.url).href);
let title = '';
if (typeof document !== 'undefined') {
// Remove the site name part from the `document.title` generated by Docusaurus
const siteTitleSuffix = ` | ${siteConfig.title}`;
title = document.title.endsWith(siteTitleSuffix)
? document.title.slice(0, -siteTitleSuffix.length)
: document.title;
}
setPageTitle(title);
// Check if the native Web Share API is available (e.g., on mobile)
if (typeof navigator !== 'undefined' && navigator.share) {
setShowNativeShare(true);
}
}, [location.pathname, siteConfig.url, siteConfig.title, propTitle]);
In the final JSX return statement, the fetched information (pageUrl
, pageTitle
) is passed as props to each share button.
// ...
return (
<div className={styles.shareContainer}>
<h4 className={styles.shareTitle}>Share</h4>
<div className={styles.buttonGroup}>
{showNativeShare && (
{/* Control the visibility of the native share button */}
)}
<TwitterShareButton url={pageUrl} title={shareQuote}>
<XIcon size={40} round />
</TwitterShareButton>
{/* ... (other buttons) ... */}
<button
onClick={handleCopyLink}
className={`${styles.shareButton} ${styles.copyButton}`}
>
{/* Toggle icon and text based on the copy state */}
{isCopied ? <Check size={24} color="green" /> : <Copy size={24} />}
<span className={styles.buttonLabel}>
{isCopied ? 'Copied!' : 'Copy'}
</span>
</button>
</div>
</div>
);
2. Styling (styles.module.css
)
For styling, I adopted CSS Modules to scope the component's styles, preventing them from affecting other elements.
The main layout is built with Flexbox, and the spacing between buttons is simply controlled with the gap
property.
.buttonGroup {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 0.75rem;
}
.shareButton {
display: inline-flex;
align-items: center;
/* ... (common styles) ... */
}
Additionally, I used an attribute selector to override the default styles of the buttons generated by react-share
.
/* Override the default styles of react-share */
.buttonGroup > button[class^="react-share__ShareButton"] {
background-color: transparent !important;
padding: 0 !important;
}
Furthermore, I implemented responsive design using media queries. The visibility of the native share and copy buttons is toggled based on the screen width. This is a deliberate choice to simplify the UI on mobile devices, as their native share functionality often includes a "copy link" option.
@media (min-width: 997px) {
/* Hide the native share button on PC */
.nativeShareButton {
display: none;
}
}
@media (max-width: 996px) {
/* Hide the copy button on mobile */
.copyButton {
display: none;
}
}
How to Use the Component
Placing the completed component into an article file (.mdx
) is very simple.
import ShareButtons from '@site/src/components/ShareButtons';
<ShareButtons />
Conclusion
By implementing this as an independent component without using swizzle
, I've increased its resilience to Docusaurus version upgrades. Additionally, by applying a scope-limited fix for the library-related type issues, I was able to create a highly maintainable component.