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

2 replies
Last updated: Nov 16, 2020
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:

  • Use the marks property in your components configuration to override how annotations (like links) are rendered
  • Use Vue's h() function with resolveComponent('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.

user S
have a look at this component . you can add a serializer for links, looking something like this:

serializers: {
        types: {
          // ...
        },
        // serialize link:
        marks: { link: LinkSerializer },
      }
serializers translate portable text into your own components.

in
LinkSerializer
, you can add
nuxt-link
, or use any link component that you have set up
Done, here is my component, for those in the needs:
<template>
  <nuxt-link :to="href" v-if="checkIfLocal"><slot /></nuxt-link>
  <a :href="href" v-else><slot /></a>
</template>

<script>
export default {
  props: ["href"],
  computed: {
    checkIfLocal() {
      if (this.href.startsWith("/")) {
        return true;
      } else {
        return false;
      }
    },
  },
};
</script>

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.

Was this answer helpful?