Joint session with Vercel: How to build intelligent storefronts (May 15th)

How to translate a local url placed inside a content block into a correct nuxt-link or routerlink?

2 repliesLast updated: Nov 29, 2025

Anyone knows how to translate a local url placed inside a content block into a correct nuxt-link or routerlink in a vue/nuxt project?
AI Update

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:

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:

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.

Show original thread
2 replies

Was this answer helpful?

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.

Related contributions