Custom Template for External Links in nuxt/content

When working with nuxt/content, you might want to have a custom template for external links. This can be useful if you want to add a custom icon or a different style to external links.

To achieve this, you can create a custom component that checks if the link is an external link and then renders the link with your custom template.

First we create a new component in our Nuxt project. nuxt/content gives us the ability to override the default components used for rendering the content by placing them in the components/content directory. So for our custom link component, we create a new file components/content/ProseA.vue.

Note: The component name must be ProseA.vue to override the default link component.

See: nuxt/content - Markdown

vue
components/content/ProseA.vue
<template>
  <NuxtLink :href="href" :target="_target">
    <slot />
  </NuxtLink>
</template>

<script setup lang="ts">
withDefaults(
  defineProps<{
    href: string;
    target?: string;
  }>(),
  {
    target: undefined,
  },
);
</script>

The code above shows the default link component.

Now that we created our custom link component, we can add a check to see if the link is an external link. To do so, we add a computed property indicating if the link is external.

vue
components/content/ProseA.vue
<template>
  <NuxtLink :href="href" :target="_target">
    <slot />
  </NuxtLink>
</template>

<script setup lang="ts">
withDefaults(
  defineProps<{
    href: string;
    target?: string;
  }>(),
  {
    target: undefined,
  },
);

/**
 * Check if the link is an external link
 * The check here is quite simple and could be more complex depending on your needs
 *
 * @returns {boolean}
 */
const isExternal = computed(() => props.href.startsWith("http"));
</script>

Since we now know if the link is external, we can add a custom icon to the link. In this example, we add a small icon to the link if it is an external link.

vue
components/content/ProseA.vue
<template>
  <NuxtLink :href="href" :target="_target">
    <slot />
    <ArrowTopRightOnSquareIcon
      v-if="isExternal"
      class="ml-1 inline-block h-5 w-5 text-gray-700"
    />
  </NuxtLink>
</template>

<script setup lang="ts">
import { ArrowTopRightOnSquareIcon } from "@heroicons/vue/20/solid";

withDefaults(
  defineProps<{
    href: string;
    target?: string;
  }>(),
  {
    target: undefined,
  },
);

/**
 * Check if the link is an external link
 * The check here is quite simple and could be more complex depending on your needs
 *
 * @returns {boolean}
 */
const isExternal = computed(() => props.href.startsWith("http"));
</script>

Conclusion and further optimizations#

With this setup, you can now add a custom icon or style to external links in your nuxt/content project. The check for external links is quite simple and could be more complex depending on your needs. One possible improvement could be to set the link target to _blank if the link is external. And also make to set the rel="noopener noreferrer" attributes to the link to improve security.

vue
components/content/ProseA.vue
<template>
  <NuxtLink
    :href="href"
    :target="_target"
    :external="isExternal"
    class="inline-flex items-center"
  >
    <slot />
    <ArrowTopRightOnSquareIcon
      v-if="isExternal"
      class="ml-1 inline-block h-5 w-5 text-gray-700"
    />
  </NuxtLink>
</template>

<script setup lang="ts">
import { ArrowTopRightOnSquareIcon } from "@heroicons/vue/20/solid";

const props = withDefaults(
  defineProps<{
    href: string;
    target?: string;
  }>(),
  {
    target: undefined,
  },
);

const isExternal = computed(() => props.href.startsWith("http"));

const _target = computed(() => {
  if (props.target) {
    return props.target;
  }

  return isExternal.value ? "_blank" : undefined;
});
</script>

NuxtLink automatically adds the rel="noopener noreferrer" attributes to external links, if the external attribute is set. In order for this to work correctly, you would have to disable the rehype-external-links plugin in your Nuxt configuration.

See: nuxt/content - Issues

js
nuxt.config.js
content: {
    markdown: {
      rehypePlugins: {
        'rehype-external-links': false,
      }
    },
},