Adding Custom Icons to Links
This page demonstrates how to use the wrapperTemplate
option of rehype-smart-links to add custom icons for different types of links.
SVG Icon Examples
Using SVG icons to add visual cues for different types of links:
Beautify Links with SVG Icons
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
wrapperTemplate: (node, linkType) => {
let icon;
if (linkType === 'internal') {
// Internal link icon - chain icon
icon = h('svg', {
xmlns: 'http://www.w3.org/2000/svg',
width: '16',
height: '16',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
class: 'icon-internal'
}, [
h('path', { d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71' }),
h('path', { d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71' })
]);
// Add to the end of the link
node.children.push(icon);
}
else if (linkType === 'external') {
// External link icon - external arrow
icon = h('svg', {
xmlns: 'http://www.w3.org/2000/svg',
width: '16',
height: '16',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
class: 'icon-external'
}, [
h('path', { d: 'M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6' }),
h('polyline', { points: '15 3 21 3 21 9' }),
h('line', { x1: '10', y1: '14', x2: '21', y2: '3' })
]);
// Add to the end of the link
node.children.push(icon);
}
else if (linkType === 'broken') {
// Broken link icon - broken chain
icon = h('svg', {
xmlns: 'http://www.w3.org/2000/svg',
width: '16',
height: '16',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
class: 'icon-broken'
}, [
h('path', { d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71' }),
h('path', { d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71' }),
h('line', { x1: '2', y1: '2', x2: '22', y2: '22' })
]);
// Add to the beginning of the link
node.children = [icon, ...node.children];
}
// Add Flex layout styles
if (icon) {
node.properties.className = [
...(node.properties.className || []),
'flex',
'items-center',
'gap-1'
];
}
return node;
}
}
]]
}
});
Icon Animation Effects
Add animation effects to icons to make links more interactive:
Icon Animation Effects
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
wrapperTemplate: (node, linkType) => {
let icon;
if (linkType === 'internal') {
// Internal link - arrow icon
icon = h('svg', {
xmlns: 'http://www.w3.org/2000/svg',
width: '16',
height: '16',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
class: 'icon-animate group-hover:scale-125 transition-transform'
}, [
h('path', { d: 'M5 12h14' }),
h('path', { d: 'M12 5L19 12L12 19' })
]);
}
else if (linkType === 'external') {
// External link - diagonal arrow
icon = h('svg', {
xmlns: 'http://www.w3.org/2000/svg',
width: '16',
height: '16',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
class: 'icon-animate group-hover:-translate-y-1 group-hover:translate-x-1 transition-transform'
}, [
h('line', { x1: '7', y1: '17', x2: '17', y2: '7' }),
h('polyline', { points: '7 7 17 7 17 17' })
]);
}
else if (linkType === 'broken') {
// Broken link - warning icon
icon = h('svg', {
xmlns: 'http://www.w3.org/2000/svg',
width: '16',
height: '16',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
class: 'icon-animate group-hover:rotate-12 transition-transform text-red-500'
}, [
h('circle', { cx: '12', cy: '12', r: '10' }),
h('line', { x1: '12', y1: '8', x2: '12', y2: '12' }),
h('line', { x1: '12', y1: '16', x2: '12.01', y2: '16' })
]);
}
// Add to node and set style classes
if (icon) {
if (linkType === 'broken') {
node.children = [icon, ...node.children];
} else {
node.children.push(icon);
}
node.properties.className = [
...(node.properties.className || []),
'flex',
'items-center',
'gap-1',
'group' // For hover state management
];
}
return node;
}
}
]]
}
});
Using Icon Libraries
You can also integrate Font Awesome or other icon libraries:
Using Font Awesome Icons
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
wrapperTemplate: (node, linkType) => {
let iconClass;
if (linkType === 'internal') {
iconClass = 'fa fa-book'; // Document icon
} else if (linkType === 'external') {
iconClass = 'fa fa-external-link'; // External link icon
} else if (linkType === 'broken') {
iconClass = 'fa fa-exclamation-triangle'; // Warning icon
}
if (iconClass) {
const icon = h('i', { class: iconClass });
// Decide icon position based on link type
if (linkType === 'broken') {
node.children = [icon, ...node.children];
} else {
node.children.push(icon);
}
// Add flex layout
node.properties.className = [
...(node.properties.className || []),
'flex',
'items-center',
'gap-2'
];
}
return node;
}
}
]]
}
});
Dynamically Select Icons Based on Site
You can dynamically select different icons based on the link destination URL:
Select Icons Based on Link Destination
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';
export default defineConfig({
markdown: {
rehypePlugins: [[
rehypeSmartLinks,
{
wrapperTemplate: (node, linkType, url) => {
let icon;
if (linkType === 'internal') {
// Select icon based on URL path
if (url.includes('/docs/')) {
// Documentation page icon
icon = h('svg', {
xmlns: 'http://www.w3.org/2000/svg',
width: '16',
height: '16',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round'
}, [
h('path', { d: 'M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z' }),
h('path', { d: 'M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z' })
]);
}
}
else if (linkType === 'external') {
// Select specific icon based on external site
if (url.includes('github.com')) {
// GitHub icon
icon = h('svg', {
xmlns: 'http://www.w3.org/2000/svg',
width: '16',
height: '16',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round'
}, [
h('path', { d: 'M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22' })
]);
}
}
else if (linkType === 'broken') {
// Broken link icon
icon = h('svg', {
xmlns: 'http://www.w3.org/2000/svg',
width: '16',
height: '16',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round'
}, [
h('path', { d: 'M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z' }),
h('path', { d: 'M13 2v7h7' }),
h('line', { x1: '8', y1: '17', x2: '16', y2: '17' }),
h('line', { x1: '8', y1: '13', x2: '14', y2: '13' }),
h('line', { x1: '8', y1: '9', x2: '10', y2: '9' }),
h('line', { x1: '4', y1: '22', x2: '20', y2: '2' })
]);
}
// Add icon and styles
if (icon) {
if (linkType === 'broken') {
node.children = [icon, ...node.children];
} else {
node.children.push(icon);
}
node.properties.className = [
...(node.properties.className || []),
'flex',
'items-center',
'gap-2'
];
}
return node;
}
}
]]
}
});
Next Steps
Explore more examples:
- Tailwind Styles - Use Tailwind CSS to customize link styles
- DaisyUI Styles - Use DaisyUI component library to customize link styles