Gatsby.js:如何将Markdown中的相对路径链接转为<Link>组件?
Absolutely! You've hit on a common pain point with using raw HTML from Markdown in Gatsby, but there are several solid solutions—including building a custom plugin for gatsby-transformer-remark, or leveraging existing tools to avoid the issue entirely. Here are your best options:
Option 1: Use react-markdown for Full Control (Recommended)
Instead of using dangerouslySetInnerHTML to render raw HTML, use react-markdown to parse your Markdown directly in React. This lets you replace native <a> tags with Gatsby's <Link> component for internal paths, while keeping external links functional.
- Install the required dependencies:
npm install react-markdown gatsby-link
- Update your component to use
react-markdowninstead of raw HTML:
import ReactMarkdown from 'react-markdown'; import { Link } from 'gatsby'; const MarkdownRenderer = ({ markdown }) => { return ( <ReactMarkdown children={markdown} components={{ a: ({ href, children }) => { // Check if the link is an internal path (starts with /, not // for external relative links) if (href && href.startsWith('/') && !href.startsWith('//')) { return <Link to={href}>{children}</Link>; } // For external links, keep the native <a> tag with safe attributes return ( <a href={href} target="_blank" rel="noopener noreferrer" > {children} </a> ); }, }} /> ); }; // Usage in your page component: <MarkdownRenderer markdown={whatever.childMarkdownRemark.rawMarkdownBody} />
This approach eliminates the security risk of dangerouslySetInnerHTML and gives you full control over how links are rendered.
Option 2: Build a Custom gatsby-transformer-remark Plugin
If you prefer to keep using dangerouslySetInnerHTML, you can build a simple plugin to tag internal links with a custom class, then add a click handler to use Gatsby's navigation.
Step 1: Create the Plugin
Make a new file at src/plugins/gatsby-remark-internal-links/index.js:
module.exports = ({ markdownAST }) => { // Traverse all link nodes in the Markdown AST markdownAST.children.forEach(node => { if (node.type === 'link' && node.url.startsWith('/') && !node.url.startsWith('//')) { // Add a custom class to internal links node.data = node.data || {}; node.data.hProperties = node.data.hProperties || {}; node.data.hProperties.className = `${node.data.hProperties.className || ''} internal-link`.trim(); } }); return markdownAST; };
Step 2: Configure the Plugin in gatsby-config.js
Add your custom plugin to the gatsby-transformer-remark options:
module.exports = { plugins: [ { resolve: 'gatsby-transformer-remark', options: { plugins: [ './src/plugins/gatsby-remark-internal-links', // Other remark plugins... ], }, }, ], };
Step 3: Add a Click Handler to Your Component
Intercept clicks on internal links and use Gatsby's navigate method:
import { navigate } from 'gatsby'; const handleInternalLinkClick = (e) => { const targetLink = e.target.closest('.internal-link'); if (targetLink) { e.preventDefault(); navigate(targetLink.getAttribute('href')); } }; // In your render method: <div dangerouslySetInnerHTML={{ __html: whatever.childMarkdownRemark.html }} onClick={handleInternalLinkClick} />
Option 3: Use gatsby-plugin-catch-links (Quickest Fix)
If you want a zero-config solution, gatsby-plugin-catch-links automatically intercepts clicks on all internal links across your site and uses Gatsby's navigation system.
- Install the plugin:
npm install gatsby-plugin-catch-links
- Add it to your
gatsby-config.js:
module.exports = { plugins: [ 'gatsby-plugin-catch-links', // Other plugins... ], };
This plugin works globally, so you don't need to modify your Markdown rendering code at all. Just install and configure it, and all internal links will preserve your SPA's behavior.
内容的提问来源于stack exchange,提问作者dougmacklin

