N
Nuxt4d ago
Bread

How to handle relative anchor links when the destination is external?

I am using an external CMS and it has some confiurable CTAS (and rich text). How can I get those relative URLS (which I can not change to be absolute) to proxy through to my CMS backend instead of hitting [..path].vue. I have a working example here: https://stackblitz.com/edit/github-5cmnvyxf?file=pages%2Findex.vue and setting up a route-proxy in /server/middleware makes this work on serverside completely fine - but navigating client-side does not (until refreshing, hitting server side).
Sean Thorne
StackBlitz
Nuxt - Starter - StackBlitz
Create a new Nuxt project, module, layer or start from a theme with our collection of starters.
3 Replies
kapa.ai
kapa.ai4d ago
Hi @Bread. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on your question about handling relative anchor links that should be treated as external, I can see you're facing a common challenge when integrating a CMS with Nuxt. Let me help you solve this issue. The Problem When you have relative URLs from your CMS that should bypass Nuxt's routing system and instead be proxied to your backend, you need to prevent <NuxtLink> from handling these as internal routes. Solution 1: Use the external prop with NuxtLink For relative links that should be treated as external (like your CMS content), you can use the external prop with <NuxtLink>:
<NuxtLink to="/your-cms-path" external>
CMS Link
</NuxtLink>
<NuxtLink to="/your-cms-path" external>
CMS Link
</NuxtLink>
This explicitly tells Nuxt to render the link as a standard HTML <a> tag, bypassing Vue Router's internal routing mechanism, as documented in the NuxtLink API docs. Solution 2: Create a custom directive for HTML content Since you're getting HTML content from a CMS that you can't modify, you might need to transform those anchor tags. You can create a custom directive to handle this, as shown in Alexander Lichter's approach:
<script setup>
const vInterpolate = {
mounted(el, binding) {
const { value } = binding
if (!value) return

// Set the HTML content
el.innerHTML = value

// Find all anchor tags
const anchors = el.querySelectorAll('a')

// Process each anchor
anchors.forEach(anchor => {
const href = anchor.getAttribute('href')

// Check if it's a relative URL that should be treated as external
if (href && href.startsWith('/') && shouldBeExternal(href)) {
// Prevent default behavior
anchor.addEventListener('click', (e) => {
e.preventDefault()
// Use navigateTo with external: true
navigateTo(href, { external: true })
})
}
})
}
}

// Function to determine if a URL should be treated as external
function shouldBeExternal(url) {
// Implement your logic to identify CMS URLs
return url.startsWith('/cms/') || url.startsWith('/api/')
}
</script>

<template>
<div v-interpolate="htmlFromCMS"></div>
</template>
<script setup>
const vInterpolate = {
mounted(el, binding) {
const { value } = binding
if (!value) return

// Set the HTML content
el.innerHTML = value

// Find all anchor tags
const anchors = el.querySelectorAll('a')

// Process each anchor
anchors.forEach(anchor => {
const href = anchor.getAttribute('href')

// Check if it's a relative URL that should be treated as external
if (href && href.startsWith('/') && shouldBeExternal(href)) {
// Prevent default behavior
anchor.addEventListener('click', (e) => {
e.preventDefault()
// Use navigateTo with external: true
navigateTo(href, { external: true })
})
}
})
}
}

// Function to determine if a URL should be treated as external
function shouldBeExternal(url) {
// Implement your logic to identify CMS URLs
return url.startsWith('/cms/') || url.startsWith('/api/')
}
</script>

<template>
<div v-interpolate="htmlFromCMS"></div>
</template>
This approach is inspired by Alexander Lichter's video on Let your anchor tags behave like Nuxt Links. Solution 3: Use a Nitro proxy endpoint For a more comprehensive solution, you can set up a Nitro proxy endpoint as recommended by Alexander Lichter in his video about The BEST way to proxy your API in Nuxt. This approach was recently fixed in Nitro 2.11.7 to properly handle relative paths with proxies, as documented in this GitHub issue. Here's how to implement it: 1. Create a server route in server/api/[...].ts:
export default defineEventHandler(async (event) => {
const url = getRequestURL(event)
// Determine if this request should be proxied
if (shouldProxyRequest(url.pathname)) {
// Proxy to your CMS backend
return proxyRequest(event, `<https://your-cms-backend.com${url.pathname}`>)
}
})

function shouldProxyRequest(path) {
// Implement your logic to identify CMS paths
return path.startsWith('/cms/') || path.startsWith('/api/')
}
export default defineEventHandler(async (event) => {
const url = getRequestURL(event)
// Determine if this request should be proxied
if (shouldProxyRequest(url.pathname)) {
// Proxy to your CMS backend
return proxyRequest(event, `<https://your-cms-backend.com${url.pathname}`>)
}
})

function shouldProxyRequest(path) {
// Implement your logic to identify CMS paths
return path.startsWith('/cms/') || path.startsWith('/api/')
}
2. Set up route rules in nuxt.config.ts:
export default defineNuxtConfig({
routeRules: {
'/cms/**': { proxy: { to: '<https://your-cms-backend.com/cms/**'> } },
'/api/**': { proxy: { to: '<https://your-cms-backend.com/api/**'> } }
}
})
export default defineNuxtConfig({
routeRules: {
'/cms/**': { proxy: { to: '<https://your-cms-backend.com/cms/**'> } },
'/api/**': { proxy: { to: '<https://your-cms-backend.com/api/**'> } }
}
})
This approach ensures that both server-side and client-side navigation to these paths will be properly proxied to your CMS backend. Conclusion Based on your specific scenario with the Stackblitz example, I recommend combining Solution 1 (using external prop) for links you can control, and Solution 3 (Nitro proxy) for a more comprehensive approach that works with both server-side and client-side navigation. The recent fix in Nitro 2.11.7 should resolve the issue you were experiencing where client-side navigation wasn't working correctly with proxied routes. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4d ago
<@280730525960896513> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Bread
BreadOP4d ago
I like 1, but I can't know that at build-time Because /some-path should not be extrernal but /media/** should be

Did you find this page helpful?