SEO-Friendly URLs in Vue
- Design route paths intentionally, whether you declare them manually or generate them through file-based routing
- Google recommends hyphens to separate words in readable URLs
- Prefer concise, descriptive URLs and a consistent case policy; Google gives no universal 60-character limit
- Paths and query parameters can both identify indexable content; retain values that change the page
URLs appear in search results before users click. /blog/vue-seo-guide tells users what to expect; /p?id=847 doesn't. Search engines also use URLs to understand page hierarchy and relevance.
Vue Router lets you declare routes manually. File-based routing integrations can generate declarations too. Either way, choose stable paths and validate their behavior on direct visits and client navigation.
Quick Setup
These are excerpts for an installed Vue Router app. Import the app-owned BlogPost and ProductPage components in your router. Install the Unhead plugin before using component head calls. For a complete rendering setup, see the Vite SSR guide or prerendering guide.
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/blog/:slug',
component: BlogPost
},
{
path: '/products/:category/:slug',
component: ProductPage
}
]
})<script setup lang="ts">
import { useHead } from '@unhead/vue'
import { useRoute } from 'vue-router'
const route = useRoute()
// This blog route treats query values as non-content tracking
useHead({
link: [{
rel: 'canonical',
href: () => new URL(route.path, 'https://mysite.com').href
}]
})
</script>The getter updates when a reused page component receives another slug. If query values change your content, retain them in its canonical; see Query Parameters. SSR and generation must serialize head output into the response.
URL Formatting Rules
Hyphens Over Underscores
Google recommends hyphens rather than underscores to help readers and search engines identify concepts. This is a readability convention, not a promise that a hyphenated page ranks higher.
✅ /performance-optimization
⚠️ /performance_optimizationVue Router implementation:
// ✅ Good
{ path: '/learn-vue-router', component: Guide }
// ❌ Bad
{ path: '/learn_vue_router', component: Guide }Lowercase Only
Google treats /About, /about, and /ABOUT as distinct URLs. They are duplicates only if your server serves duplicate or similar content there. Vue Router matches paths case-insensitively by default; its sensitive option and your host can change that behavior.
✅ /about
✅ /products/phones
❌ /About
❌ /products/PhonesChoose a case policy for new paths and use redirects for intentional URL changes. Do not lowercase a case-sensitive asset or endpoint blindly.
Keep URLs Short
Google recommends simple, descriptive URLs. There is no universal 60-character ranking or display rule. Remove unnecessary words and parameters while keeping a useful hierarchy.
When longer URLs make sense:
✅ /docs/getting-started/installation-guide (clear hierarchy)
✅ /blog/2025/fixing-vue-hydration-mismatch (date + topic)
❌ /the-ultimate-comprehensive-complete-guide (keyword stuffing)Keywords Near the Start
Google's own guidance is that a URL should describe the page's content clearly so users and crawlers can identify the concept at a glance. Put useful descriptive words where readers can recognize them. Do not infer a fixed ranking benefit from keyword position.
✅ /vue-router-seo-guide
✅ /seo/vue-best-practices
❌ /guides-and-tutorials-for-seo-in-vue-routerBut don't sacrifice readability:
// ✅ Natural keyword placement
{ path: '/vue-seo/:topic', component: Guide }
// ❌ Keyword stuffing
{ path: '/vue-seo-guide-vue-router-seo-tutorial', component: Guide }Avoid Dates (Usually)
Dates can make an evergreen URL appear tied to an older edition. They do not prevent content updates. Decide whether that edition or date is part of the page identity before choosing a pattern.
❌ /blog/2024/vue-router-guide (looks stale)
❌ /blog/2024/12/17/post-title (ties the URL to an edition)
✅ /blog/vue-router-guide (can be updated anytime)Exception: Time-sensitive content like news, events, changelogs:
const routes = [
// News/events - dates make sense
{ path: '/changelog/:year/:month/:slug', component: Changelog },
{ path: '/events/2025/:slug', component: Event },
// Evergreen content - skip dates
{ path: '/blog/:slug', component: BlogPost },
{ path: '/guides/:slug', component: Guide }
]A stable URL lets you update a page without moving its existing links. If you change an established URL, plan and verify the redirect rather than expecting ranking preservation.
Path Segments vs Query Parameters
Google supports both paths and standard query parameters. Choose a readable format. A category can use /products/phones or /products?category=phones; neither structure guarantees indexing or rankings.
Sorting can repeat content, while pagination or a filter can change the items shown. Consolidate duplicate or similar variants, and keep useful distinct content addressable. See Query Parameters for those different policies.
Vue Router Path Segments
These app-owned component route excerpts show possible content hierarchies:
const routes = [
// ✅ Path segments for SEO
{
path: '/products/:category/:slug',
component: Product
},
{
path: '/blog/:year/:month/:slug',
component: BlogPost
},
{
path: '/docs/:section/:page',
component: Documentation
}
]These patterns match paths such as:
/products/phones/iphone-15/blog/2025/12/vue-seo-guide/docs/getting-started/installation
Query parameters can represent sorting, filtering, and pagination. Retain meaningful values in canonicals; consolidate only duplicate or similar pages. See the Query Parameters guide.
Dynamic Routes
Vue Router dynamic routes match parameters such as slugs. Descriptive slugs can help readers identify a page; they do not establish that /products/laptop-pro-15 ranks above /products/84792.
Basic Dynamic Segments
const routes = [
{
path: '/blog/:slug',
component: BlogPost,
props: true
}
]With props: true, the router passes the slug to your app-owned BlogPost component. Load the article through your application's data layer and pass verified title and description values to Unhead. useFetch() is not an auto-imported Vue API. For server-rendered data, also transfer the same initial payload to the client; the route declaration alone does not implement that pipeline.
Nested Dynamic Routes
const routes = [
{
path: '/products/:category/:slug',
component: Product,
props: true
}
]Matches hierarchical paths such as:
/products/electronics/laptop/products/clothing/jacket
The route passes category and slug props to your app-owned Product component. Handle missing data and changed params in your loader; a reused component does not necessarily remount when a parameter changes. Verify its content and head output after navigation.
Multiple Optional Parameters
const routes = [
{
path: '/search/:query/:filters?',
component: SearchResults
}
]Matches both:
/search/vue-router/search/vue-router/recent
Optional parameters can identify different results or duplicate variants. Compare the content before selecting a canonical. Do not remove the optional segment if it distinguishes a useful page.
Slug Generation
kebabCase from scule converts case-delimited words. After installing scule, this supported example converts an existing identifier:
import { kebabCase } from 'scule'
kebabCase('vueRouterGuide') // "vue-router-guide"It is not a general title-to-URL sanitizer: spaces and punctuation need a separate policy. Store a stable CMS slug, check uniqueness, and preserve redirects when you intentionally change it.
For international content, Google supports UTF-8 URLs but they must be percent-encoded when sent over HTTP. Consider whether your audience expects localized slugs or ASCII-only.
URL Hierarchy and Internal Linking
Organize related pages and link to important content. URL segment count is not click depth: a long path can have a direct homepage link, while a short path can be orphaned. Do not infer a fixed importance score from four path levels or a universal three-click rule. See internal linking.
When generating pages at scale, use URL patterns that include target keywords:
const routes = [
{ path: '/compare/:slug', component: ComparePage },
{ path: '/alternatives/:slug', component: AlternativesPage },
{ path: '/features/:slug', component: FeaturePage },
]Each pattern targets a different keyword cluster. Keep slugs descriptive: /compare/pocketui-vs-css-peeper rather than /compare/1, so both users and crawlers know what the page compares before they open it.
Server Configuration
For a client-only app using createWebHistory(), direct requests to valid client routes need the app shell. Follow Vue Router's host configuration examples for your actual server and base path.
Serve assets and API routes separately. A catch-all HTTP 200 app shell can turn missing pages into soft 404s. SSR and generated static files need their own routing behavior; do not replace their route-specific HTML with a generic shell. Test a valid direct URL, a missing route, and an asset over HTTP.
Testing URL Structure
Check direct requests and navigation: open two declared routes directly, then navigate between them. Inspect HTTP status, actual page content, and the canonical after each transition. A site: query is not a complete index inventory or a prediction of how Google will display a URL.
Check canonicalization:
Use Google Search Console URL Inspection to verify:
- User-declared canonical matches your intent
- Google-selected canonical agrees with your preference
- No conflicting signals from redirects or alternates
Checklist
- URLs use hyphens, not underscores, to separate words
- Paths follow a deliberate case policy without breaking case-sensitive resources
- Useful distinct content has stable, crawlable URLs, with meaningful query values retained
- Canonicals identify the intended page; duplicate and distinct variants have different policies
- Date segments appear only when they are part of the intended page identity
- Slugs are descriptive (
laptop-pro-15, not84792) - Valid direct routes work, while missing routes and assets receive appropriate responses
Nuxt supplies file-based routing and rendering APIs. Canonical and sitemap behavior requires the relevant modules and configuration. Learn more about URL structure in Nuxt →
Pre-launch setup, post-launch verification, and ongoing monitoring. Interactive checklist with links to every guide.