Table Of Contents
We’ve all been there: you’re writing a blog post in Markdown, you link to an external resource, and you realize you have to manually add target="_blank" if you don’t want readers to bounce from your site. In Astro, we can automate this using Rehype plugins, but most “quick fixes” you find online either break your local dev links or ignore screen reader users entirely.
I wanted a solution that was smart enough to know which domains are “mine” and accessible enough to warn users when a new tab is about to pop open.
The Problem with Simple String Matching
Most basic plugins just look for href.startsWith('http'). The issue? If you link to your own production site from a local dev environment, it gets treated as an external link. Even worse, if you link to a URL that happens to contain your domain name in the path, simple .includes() checks can fail.
The Solution: A Robust Rehype Plugin
This plugin uses the proper URL API to parse hostnames and compares them against an array of internal domains. It also injects a hidden <span> inside the link. This is a cleaner alternative to aria-describedby because it keeps the announcement self-contained within the link tag—meaning screen readers will announce something like “Visualizer (opens in a new tab), link” automatically.
You can find the full code in this Gist (opens in a new tab).
What this code actually does
- Domain Whitelisting: It accepts a
domainsarray. It checks the link’s hostname against this list (and subdomains). If you’re onlocalhostor your staging URL, it stays in the same tab. - Safety First: It automatically appends
rel="noopener noreferrer"to prevent security risks associated withtarget="_blank". - A11y-Friendly: It injects a
spanwith an.sr-onlyclass. If you have this class in your CSS (which you should!), sighted users see nothing, but screen reader users get the necessary context that they are leaving the current page. - Respects Manual Input: If you’ve already manually added a
targetattribute to a link in your Markdown, the plugin steps aside and lets your manual choice take precedence.
The Setup
Drop the plugin into your src/lib/plugins/ folder and hook it up in your astro.config.mjs:
// astro.config.mjs
import { externalLink } from './src/lib/plugins/externalLink';
export default defineConfig({
markdown: {
rehypePlugins: [
[
externalLink,
{
domains: ["localhost", "mysite.com", "staging.mysite.app"],
},
],
],
},
});
Credits & Inspiration
This solution is a hybrid evolution of some great community work. If you want to see the different approaches that led to this one, check out these posts:
Davide Salvagni: For the initial foundational logic on opening external links in a new tab (opens in a new tab).
Niko from Project Brackets: For the deep dive into Astro Markdown target blank solutions (opens in a new tab) and pushing for better accessibility standards.
And yes, their sites open in a new tab. ;)