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
currentColorto 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.
This is excellent. My only problem is that putting the
classreference in adivinside theanchormeans the icon is presented below the anchor's text. What needs to be specified to ensure that the icon shows immediately to the right of the text? Grateful any guidance.