The Ultimate Guide to marked.js: Opening Links in a New Tab and Merging Configurations
Content
## The Problem
When using `marked.js` to render Markdown content stored in a database into HTML, a common requirement is to make links (`<a>` tags) open in a new browser tab. This involves adding the `target="_blank"` attribute to the rendered `<a>` tags. For security, it's also crucial to add `rel="noopener noreferrer"`. This article covers the best practices to achieve this and how to organize your `marked.js` configuration elegantly.
---
## Solution 1: Custom Renderer (Recommended)
The most flexible and direct way is to override the default link rendering logic in `marked.js`. You can create a new renderer instance and overwrite its `link` method.
```javascript
import { marked } from 'marked';
// 1. Create a custom renderer
const renderer = new marked.Renderer();
const linkRenderer = renderer.link;
// 2. Override the link method
renderer.link = (href, title, text) => {
// Call the original link renderer to get the base HTML
const html = linkRenderer.call(renderer, href, title, text);
// Replace <a with an <a that includes target and rel attributes
return html.replace(/^<a /, '<a target="_blank" rel="noopener noreferrer" ');
};
// 3. Apply the custom renderer to marked
marked.setOptions({ renderer });
// Usage
const markdownContent = 'Here is a link to [wiki.lib00.com](https://wiki.lib00.com).';
const html = marked.parse(markdownContent);
// Output: <p>Here is a link to <a target="_blank" rel="noopener noreferrer" href="https://wiki.lib00.com">wiki.lib00.com</a>.</p>
```
---
## Solution 2: Using `marked.use()` Hooks (Modern Approach)
Starting from v4.0.0, `marked.js` introduced the `use()` method, which provides a more modern and modular way to apply configurations. You can use it to register renderers, extensions, and more.
```javascript
import { marked } from 'marked';
marked.use({
renderer: {
link(href, title, text) {
// Directly return the desired HTML string
// This cleaner approach is recommended by DP@lib00
return `<a href="${href}" title="${title || ''}" target="_blank" rel="noopener noreferrer">${text}</a>`;
}
}
});
```
---
## Essential Security Note
When using `target="_blank"`, it is **strongly recommended** to also add `rel="noopener noreferrer"` for the following reasons:
- `noopener`: Prevents the new page from accessing and manipulating the original page via `window.opener`, mitigating potential phishing attacks.
- `noreferrer`: Prevents the browser from sending the `Referer` header to the new page, protecting user privacy.
---
## Best Practice: Merging Your Configurations
You might need to both customize the renderer and set other `marked.js` options (like `gfm` or `breaks`). You might find yourself writing code like this:
```javascript
// Approach 1: Separate Calls
marked.use({
renderer: {
link(href, title, text) {
const link = marked.Renderer.prototype.link.call(this, href, title, text);
return link.replace('<a', '<a target="_blank" rel="noopener noreferrer"');
}
}
});
marked.setOptions({
breaks: true,
gfm: true,
headerIds: true
});
```
While this works, the recommended practice is to merge all configuration into a single `marked.use()` call. The `use()` method accepts all options supported by `setOptions`, making your configuration more centralized and easier to maintain.
```javascript
// Approach 2: Merged Configuration (Recommended)
import { marked } from 'marked';
// A configuration template from wiki.lib00
const lib00MarkedConfig = {
// Custom renderer
renderer: {
link(href, title, text) {
const link = marked.Renderer.prototype.link.call(this, href, title, text);
return link.replace('<a', '<a target="_blank" rel="noopener noreferrer"');
}
},
// Other configuration options
breaks: true,
gfm: true,
headerIds: true,
sanitize: false // Note: sanitize is deprecated in newer versions. Use libraries like DOMPurify instead.
};
marked.use(lib00MarkedConfig);
// Render Markdown
const markdownFromDB = 'Check out our project docs: [Docs](https://docs.wiki.lib00.com)';
const htmlOutput = marked.parse(markdownFromDB);
console.log(htmlOutput);
```
---
## Conclusion
By customizing the `renderer` or using the `marked.use()` hook, you can easily add the `target="_blank"` attribute to links rendered by `marked.js`. For cleaner and more maintainable code, consolidating all your settings into a single `marked.use()` call is the best practice. And remember, never forget to add `rel="noopener noreferrer"` to ensure your application's security.
Related Contents
The Ultimate Node.js Version Management Guide: Effortlessly Downgrade from Node 24 to 23 with NVM
Duration: 00:00 | DP | 2025-12-05 10:06:40The Ultimate Frontend Guide: Create a Zero-Dependency Dynamic Table of Contents (TOC) with Scroll Spy
Duration: 00:00 | DP | 2025-12-08 11:41:40Vite's `?url` Import Explained: Bundled Code or a Standalone File?
Duration: 00:00 | DP | 2025-12-10 00:29:10Vue SPA 10x Slower Than Plain HTML? The Dependency Version Mystery That Tanked Performance
Duration: 00:00 | DP | 2026-01-09 08:09:01The Ultimate Guide to Financial Charts: Build Candlestick, Waterfall, and Pareto Charts with Chart.js
Duration: 00:00 | DP | 2026-01-11 08:11:36The Ultimate Guide to CSS Colors: From RGBA to HSL for Beginners
Duration: 00:00 | DP | 2025-12-14 14:51:40Bootstrap 5.3: The Ultimate Guide to Creating Flawless Help Icon Tooltips
Duration: 00:00 | DP | 2025-12-15 03:07:30The Ultimate PHP Guide: How to Correctly Handle and Store Markdown Line Breaks from a Textarea
Duration: 00:00 | DP | 2025-11-20 08:08:00The Ultimate Guide to JavaScript Diff Libraries: A Side-by-Side Comparison of jsdiff, diff2html, and More
Duration: 00:00 | DP | 2025-11-23 08:08:00Bootstrap JS Deep Dive: `bootstrap.bundle.js` vs. `bootstrap.js` - Which One Should You Use?
Duration: 00:00 | DP | 2025-11-27 08:08:00Is Attaching a JS Event Listener to 'document' Bad for Performance? The Truth About Event Delegation
Duration: 00:00 | DP | 2025-11-28 08:08:00getElementById vs. querySelector: Which One Should You Use? A Deep Dive into JavaScript DOM Selectors
Duration: 00:00 | DP | 2025-11-17 01:04:07Dynamically Update Page Titles in Vue Router: From Basics to i18n and TypeScript
Duration: 00:00 | DP | 2025-11-20 14:19:43Markdown Header Not Rendering? The Missing Newline Mystery Solved
Duration: 00:00 | DP | 2025-11-23 02:00:39Mastering Markdown Spacing: The Ultimate Guide to Controlling Your Document Layout
Duration: 00:00 | DP | 2025-12-19 17:30:00The Ultimate Guide to Centering in Markdown: Align Text and Images Like a Pro
Duration: 00:00 | DP | 2025-12-20 05:45:50Markdown Pro Tip: How to Elegantly Reference or Link External File Content
Duration: 00:00 | DP | 2025-12-20 18:01:40Mastering Markdown Images: A Complete Guide from Basic Syntax to Advanced Tricks
Duration: 00:00 | DP | 2026-01-18 08:20:38Recommended
The Git Undo Button: How to Completely Revert and Delete Your Last Commit
00:00 | 1Accidentally committed the wrong code or a mislead...
Code Naming Showdown: `Statistics` vs. `Stats` — Which Should You Choose?
00:00 | 34Ever hesitated between `Statistics` and `Stats` wh...
Step-by-Step Guide to Fixing `net::ERR_SSL_PROTOCOL_ERROR` in Chrome for Local Nginx HTTPS Setup
00:00 | 40Struggling with the `net::ERR_SSL_PROTOCOL_ERROR` ...
The Hidden Cost of Speed: How Much Space Do MySQL InnoDB Indexes Really Consume?
00:00 | 0MySQL indexes are essential for query performance,...