When building websites, it's common practice to indicate when links will open in a new tab or window (target="_blank"
). This guide examines multiple approaches to automatically add external link icons, exploring the strengths and limitations of each method.
Convert this SVG icon:
<svg viewbox="0 0 48 48">
<path d="M36 24c-1.2 0-2 0.8-2 2v12c0 1.2-0.8 2-2 2h-22c-1.2 0-2-0.8-2-2v-22c0-1.2 0.8-2 2-2h12c1.2 0 2-0.8 2-2s-0.8-2-2-2h-12c-3.4 0-6 2.6-6 6v22c0 3.4 2.6 6 6 6h22c3.4 0 6-2.6 6-6v-12c0-1.2-0.8-2-2-2z"></path>
<path d="M43.8 5.2c-0.2-0.4-0.6-0.8-1-1-0.2-0.2-0.6-0.2-0.8-0.2h-12c-1.2 0-2 0.8-2 2s0.8 2 2 2h7.2l-18.6 18.6c-0.8 0.8-0.8 2 0 2.8 0.4 0.4 0.8 0.6 1.4 0.6s1-0.2 1.4-0.6l18.6-18.6v7.2c0 1.2 0.8 2 2 2s2-0.8 2-2v-12c0-0.2 0-0.6-0.2-0.8z"></path>
</svg>
...into a CSS implementation that automatically appears on external links.
The simplest approach is to convert the SVG paths directly into a CSS clip-path
property.
.external-link-icon {
clip-path: path('M36 24c-1.2 0-2 0.8-2 2v12c0 1.2-0.8 2-2 2h-22c-1.2 0-2-0.8-2-2v-22c0-1.2 0.8-2 2-2h12c1.2 0 2-0.8 2-2s-0.8-2-2-2h-12c-3.4 0-6 2.6-6 6v22c0 3.4 2.6 6 6 6h22c3.4 0 6-2.6 6-6v-12c0-1.2-0.8-2-2-2zM43.8 5.2c-0.2-0.4-0.6-0.8-1-1-0.2-0.2-0.6-0.2-0.8-0.2h-12c-1.2 0-2 0.8-2 2s0.8 2 2 2h7.2l-18.6 18.6c-0.8 0.8-0.8 2 0 2.8 0.4 0.4 0.8 0.6 1.4 0.6s1-0.2 1.4-0.6l18.6-18.6v7.2c0 1.2 0.8 2 2 2s2-0.8 2-2v-12c0-0.2 0-0.6-0.2-0.8z');
}
<a href="https://example.com">
Link with icon <div class="external-link-icon"></div>
</a>
- Simple, direct conversion
- Works with a single div element
- Poor browser support (only modern browsers support the
path()
function) - Combines two SVG paths into one, which can cause rendering issues
- Requires manual insertion of the icon element
- Difficult to adjust the color (requires background color manipulation)
A more accurate approach is to split the implementation into two pseudo-elements, each handling one path.
.external-link-icon {
position: relative;
width: 48px;
height: 48px;
background-color: transparent;
}
.external-link-icon::before,
.external-link-icon::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #0066cc;
}
/* Box part */
.external-link-icon::before {
clip-path: path('M36 24c-1.2 0-2 0.8-2 2v12c0 1.2-0.8 2-2 2h-22c-1.2 0-2-0.8-2-2v-22c0-1.2 0.8-2 2-2h12c1.2 0 2-0.8 2-2s-0.8-2-2-2h-12c-3.4 0-6 2.6-6 6v22c0 3.4 2.6 6 6 6h22c3.4 0 6-2.6 6-6v-12c0-1.2-0.8-2-2-2z');
}
/* Arrow part */
.external-link-icon::after {
clip-path: path('M43.8 5.2c-0.2-0.4-0.6-0.8-1-1-0.2-0.2-0.6-0.2-0.8-0.2h-12c-1.2 0-2 0.8-2 2s0.8 2 2 2h7.2l-18.6 18.6c-0.8 0.8-0.8 2 0 2.8 0.4 0.4 0.8 0.6 1.4 0.6s1-0.2 1.4-0.6l18.6-18.6v7.2c0 1.2 0.8 2 2 2s2-0.8 2-2v-12c0-0.2 0-0.6-0.2-0.8z');
}
- More accurate representation of the original SVG
- Each path is handled separately, improving rendering
- Still has poor browser support (requires modern browsers)
- Still requires manual insertion of icon element
- Complex CSS structure
- Difficult to scale properly
For automatic application to external links, we can use CSS masks with attribute selectors.
a[target="_blank"] {
position: relative;
padding-right: 1.5em;
}
a[target="_blank"]::after {
content: '';
position: absolute;
width: 1em;
height: 1em;
right: 0;
top: 50%;
transform: translateY(-50%);
background-color: currentColor;
mask: path('M36 24c-1.2 0-2 0.8-2 2v12c0 1.2-0.8 2-2 2h-22c-1.2 0-2-0.8-2-2v-22c0-1.2 0.8-2 2-2h12c1.2 0 2-0.8 2-2s-0.8-2-2-2h-12c-3.4 0-6 2.6-6 6v22c0 3.4 2.6 6 6 6h22c3.4 0 6-2.6 6-6v-12c0-1.2-0.8-2-2-2z M43.8 5.2c-0.2-0.4-0.6-0.8-1-1-0.2-0.2-0.6-0.2-0.8-0.2h-12c-1.2 0-2 0.8-2 2s0.8 2 2 2h7.2l-18.6 18.6c-0.8 0.8-0.8 2 0 2.8 0.4 0.4 0.8 0.6 1.4 0.6s1-0.2 1.4-0.6l18.6-18.6v7.2c0 1.2 0.8 2 2 2s2-0.8 2-2v-12c0-0.2 0-0.6-0.2-0.8z');
-webkit-mask: path('M36 24c-1.2 0-2 0.8-2 2v12c0 1.2-0.8 2-2 2h-22c-1.2 0-2-0.8-2-2v-22c0-1.2 0.8-2 2-2h12c1.2 0 2-0.8 2-2s-0.8-2-2-2h-12c-3.4 0-6 2.6-6 6v22c0 3.4 2.6 6 6 6h22c3.4 0 6-2.6 6-6v-12c0-1.2-0.8-2-2-2z M43.8 5.2c-0.2-0.4-0.6-0.8-1-1-0.2-0.2-0.6-0.2-0.8-0.2h-12c-1.2 0-2 0.8-2 2s0.8 2 2 2h7.2l-18.6 18.6c-0.8 0.8-0.8 2 0 2.8 0.4 0.4 0.8 0.6 1.4 0.6s1-0.2 1.4-0.6l18.6-18.6v7.2c0 1.2 0.8 2 2 2s2-0.8 2-2v-12c0-0.2 0-0.6-0.2-0.8z');
}
- Automatically applies to external links
- Uses
currentColor
to match the text color - No need for manual icon insertion
- Scales with font size (using em units)
- CSS masks have limited browser support
- Complex SVG paths are especially problematic in masks
- Rendering issues in some browsers (appearing as blue squares)
- Requires both standard and vendor-prefixed properties
A more compatible approach uses SVG as a background image via data URI, with CSS filters for color control.
a[target="_blank"] {
position: relative;
padding-right: 1.2em;
}
a[target="_blank"]::after {
content: '';
position: absolute;
width: 0.9em;
height: 0.9em;
right: 0;
top: 50%;
transform: translateY(-50%);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Cpath d='M36 24c-1.2 0-2 0.8-2 2v12c0 1.2-0.8 2-2 2h-22c-1.2 0-2-0.8-2-2v-22c0-1.2 0.8-2 2-2h12c1.2 0 2-0.8 2-2s-0.8-2-2-2h-12c-3.4 0-6 2.6-6 6v22c0 3.4 2.6 6 6 6h22c3.4 0 6-2.6 6-6v-12c0-1.2-0.8-2-2-2z'/%3E%3Cpath d='M43.8 5.2c-0.2-0.4-0.6-0.8-1-1-0.2-0.2-0.6-0.2-0.8-0.2h-12c-1.2 0-2 0.8-2 2s0.8 2 2 2h7.2l-18.6 18.6c-0.8 0.8-0.8 2 0 2.8 0.4 0.4 0.8 0.6 1.4 0.6s1-0.2 1.4-0.6l18.6-18.6v7.2c0 1.2 0.8 2 2 2s2-0.8 2-2v-12c0-0.2 0-0.6-0.2-0.8z'/%3E%3C/svg%3E");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
filter: brightness(0) invert(0.7);
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
a[target="_blank"]::after {
filter: brightness(0) invert(1);
}
}
/* Change color on hover */
a[target="_blank"]:hover::after {
filter: brightness(0) invert(0.5) sepia(1) saturate(5) hue-rotate(175deg);
}
- Much better browser compatibility
- Still automatically applies to external links
- Color control via CSS filters
- Dark mode and hover state support
- Scales with font size
- CSS filters can have inconsistent results across browsers
- Complex filters are harder to understand/maintain
- Filter-based coloring is less intuitive than direct color properties
The most reliable approach embeds colors directly in the SVG and uses inline-flex for alignment.
a[target="_blank"] {
position: relative;
display: inline-flex;
align-items: center;
}
a[target="_blank"]::after {
content: '';
display: inline-block;
position: relative;
width: 0.9em;
height: 0.9em;
margin-left: 0.3em;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Cpath fill='%23666666' d='M36 24c-1.2 0-2 0.8-2 2v12c0 1.2-0.8 2-2 2h-22c-1.2 0-2-0.8-2-2v-22c0-1.2 0.8-2 2-2h12c1.2 0 2-0.8 2-2s-0.8-2-2-2h-12c-3.4 0-6 2.6-6 6v22c0 3.4 2.6 6 6 6h22c3.4 0 6-2.6 6-6v-12c0-1.2-0.8-2-2-2z'/%3E%3Cpath fill='%23666666' d='M43.8 5.2c-0.2-0.4-0.6-0.8-1-1-0.2-0.2-0.6-0.2-0.8-0.2h-12c-1.2 0-2 0.8-2 2s0.8 2 2 2h7.2l-18.6 18.6c-0.8 0.8-0.8 2 0 2.8 0.4 0.4 0.8 0.6 1.4 0.6s1-0.2 1.4-0.6l18.6-18.6v7.2c0 1.2 0.8 2 2 2s2-0.8 2-2v-12c0-0.2 0-0.6-0.2-0.8z'/%3E%3C/svg%3E");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
a[target="_blank"]::after {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Cpath fill='%23cccccc' d='M36 24c-1.2 0-2 0.8-2 2v12c0 1.2-0.8 2-2 2h-22c-1.2 0-2-0.8-2-2v-22c0-1.2 0.8-2 2-2h12c1.2 0 2-0.8 2-2s-0.8-2-2-2h-12c-3.4 0-6 2.6-6 6v22c0 3.4 2.6 6 6 6h22c3.4 0 6-2.6 6-6v-12c0-1.2-0.8-2-2-2z'/%3E%3Cpath fill='%23cccccc' d='M43.8 5.2c-0.2-0.4-0.6-0.8-1-1-0.2-0.2-0.6-0.2-0.8-0.2h-12c-1.2 0-2 0.8-2 2s0.8 2 2 2h7.2l-18.6 18.6c-0.8 0.8-0.8 2 0 2.8 0.4 0.4 0.8 0.6 1.4 0.6s1-0.2 1.4-0.6l18.6-18.6v7.2c0 1.2 0.8 2 2 2s2-0.8 2-2v-12c0-0.2 0-0.6-0.2-0.8z'/%3E%3C/svg%3E");
}
}
/* Hover state */
a[target="_blank"]:hover::after {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Cpath fill='%230066cc' d='M36 24c-1.2 0-2 0.8-2 2v12c0 1.2-0.8 2-2 2h-22c-1.2 0-2-0.8-2-2v-22c0-1.2 0.8-2 2-2h12c1.2 0 2-0.8 2-2s-0.8-2-2-2h-12c-3.4 0-6 2.6-6 6v22c0 3.4 2.6 6 6 6h22c3.4 0 6-2.6 6-6v-12c0-1.2-0.8-2-2-2z'/%3E%3Cpath fill='%230066cc' d='M43.8 5.2c-0.2-0.4-0.6-0.8-1-1-0.2-0.2-0.6-0.2-0.8-0.2h-12c-1.2 0-2 0.8-2 2s0.8 2 2 2h7.2l-18.6 18.6c-0.8 0.8-0.8 2 0 2.8 0.4 0.4 0.8 0.6 1.4 0.6s1-0.2 1.4-0.6l18.6-18.6v7.2c0 1.2 0.8 2 2 2s2-0.8 2-2v-12c0-0.2 0-0.6-0.2-0.8z'/%3E%3C/svg%3E");
}
/* Fallback for very old browsers */
@supports not (background-image: url("data:image/svg+xml,%3Csvg")) {
a[target="_blank"]::after {
content: "↗";
display: inline-block;
margin-left: 0.2em;
width: auto;
height: auto;
background-image: none;
}
}
/* Mobile responsiveness */
@media (max-width: 480px) {
a[target="_blank"] {
word-break: break-word;
}
}
- Maximum browser compatibility
- Consistent rendering across browsers
- Reliable display (always visible, not just on hover)
- Better text alignment with inline-flex
- Different colors for different states baked into the SVG
- Simple fallback for older browsers
- Mobile-friendly with word-break handling
- Need to create separate SVGs for each state (normal, dark mode, hover)
- Larger CSS footprint with multiple data URIs
- Cannot use CSS variables directly for colors (need to update the SVG)
Approach | Browser Support | Automatic | Color Control | Complexity | Reliability |
---|---|---|---|---|---|
1. Basic clip-path | Poor | No | Limited | Low | Poor |
2. Split pseudo-elements | Poor | No | Limited | Medium | Medium |
3. CSS masks | Limited | Yes | Good | Medium | Poor |
4. Data URI with filters | Good | Yes | Good | Medium | Medium |
5. Data URI with embedded colors | Excellent | Yes | Good | Medium | Excellent |
- For maximum compatibility and reliability: Use Approach 5 (SVG as data URI with embedded colors)
- For projects with modern browser requirements: Approach 3 or 4 can be suitable
- For simple cases or where you need to manually place icons: Approach 2 might be sufficient
- For all CSS-only implementations without external image files: Approach 5 is still recommended
When implementing external link icons, consider:
- Accessibility: Ensure icon appearance doesn't confuse screen readers
- Usability: Icons should be clearly visible but not distracting
- Performance: Data URIs increase CSS size but save HTTP requests
- Maintenance: Choose an approach that's easy to update with your workflow
By understanding these different techniques, you can choose the best approach for your specific project requirements and browser support needs.