How to translate a local url placed inside a content block into a correct nuxt-link or routerlink?
I can help you with translating local URLs in Portable Text to proper Nuxt links in a Vue/Nuxt project. Based on the Sanity documentation, here's how to handle this:
Using Custom Serializers for Links in Vue/Nuxt
When rendering Portable Text in a Vue/Nuxt application, you need to create custom serializers (called "components" in the newer libraries) to handle link annotations and convert them to <NuxtLink> or Vue Router's <RouterLink>.
1. Install the Vue Portable Text Library
First, make sure you're using the @portabletext/vue package for rendering Portable Text in your Vue/Nuxt project.
2. Create Custom Mark Components
You'll need to define custom components for your link marks. Here's an example of how to handle internal links and convert them to <NuxtLink> components:
<script setup>
import { PortableText } from '@portabletext/vue'
import { h, resolveComponent } from 'vue'
const components = {
marks: {
// Handle internal links - convert to NuxtLink
internalLink: ({ value, children }) => {
return h(resolveComponent('NuxtLink'), {
to: value.slug?.current ? `/${value.slug.current}` : '/'
}, () => children)
},
// Handle external links - use regular anchor
link: ({ value, children }) => {
return h('a', {
href: value.href,
target: value.blank ? '_blank' : undefined,
rel: value.blank ? 'noopener noreferrer' : undefined
}, children)
}
}
}
</script>
<template>
<PortableText :value="content" :components="components" />
</template>3. Query the Referenced Data with GROQ
Make sure your GROQ query joins the referenced document data so you have access to the slug:
*[_type == "post"]{
...,
body[]{
...,
markDefs[]{
...,
_type == "internalLink" => {
"slug": @.reference->slug
}
}
}
}4. Schema Configuration
Your Portable Text schema should define the link annotation like this:
marks: {
annotations: [
{
name: 'internalLink',
type: 'object',
title: 'Internal link',
fields: [
{
name: 'reference',
type: 'reference',
title: 'Reference',
to: [{ type: 'page' }, { type: 'post' }]
}
]
}
]
}Key Points:
- Use the
marksproperty in your components configuration to override how annotations (like links) are rendered - Use Vue's
h()function withresolveComponent('NuxtLink')to create NuxtLink components programmatically - Always join referenced data in your GROQ queries using the
@.reference->syntax to get the slug or path information - Handle internal links (using NuxtLink) and external links (using regular anchor tags) separately for optimal routing
This approach gives you full control over how links are rendered and ensures internal navigation uses Nuxt's router for optimal performance with client-side routing, while external links work as standard anchors.
For more details, check out the Portable Text Vue serializer documentation and Sanity's guide on presenting Portable Text.
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.