Skip to main content

Installation

Ziggy includes a Vue plugin that makes the route() helper available throughout your Vue application—in templates, component scripts, and the Options API.

Basic Setup with @routes

If you’re using the @routes Blade directive, Ziggy’s config is available globally. Simply install the plugin:
import { createApp } from 'vue';
import { ZiggyVue } from 'ziggy-js';
import App from './App.vue';

createApp(App)
    .use(ZiggyVue)
    .mount('#app');

Setup Without @routes

If you’re not using @routes, import and pass Ziggy’s configuration to the plugin:
import { createApp } from 'vue';
import { ZiggyVue } from 'ziggy-js';
import { Ziggy } from './ziggy.js'; // Generated by php artisan ziggy:generate
import App from './App.vue';

createApp(App)
    .use(ZiggyVue, Ziggy)
    .mount('#app');

Usage in Templates

Once the plugin is installed, use route() directly in your templates:
<template>
    <nav>
        <a :href="route('home')" class="nav-link">Home</a>
        <a :href="route('posts.index')" class="nav-link">Blog</a>
        <a :href="route('posts.show', { post: post.id })" class="nav-link">
            {{ post.title }}
        </a>
    </nav>
</template>

<script>
export default {
    props: ['post'],
};
</script>

Vue 3 Composition API

The ZiggyVue plugin provides the route() function via Vue’s dependency injection system.

Using inject() with <script setup>

In Vue 3’s <script setup>, use inject() to access the route() function:
<template>
    <div>
        <h1>{{ post.title }}</h1>
        <a :href="route('posts.index')" class="back-link">
            ← Back to all posts
        </a>
    </div>
</template>

<script setup>
import { inject } from 'vue';
import { ref } from 'vue';

const route = inject('route');
const props = defineProps(['postId']);

// Use route() in your script
const fetchPost = async () => {
    const response = await fetch(route('api.posts.show', { post: props.postId }));
    return response.json();
};
</script>

Standard Composition API

<template>
    <a :href="postUrl" class="post-link">View Post</a>
</template>

<script>
import { inject, computed } from 'vue';

export default {
    props: ['post'],
    setup(props) {
        const route = inject('route');
        
        const postUrl = computed(() => {
            return route('posts.show', { post: props.post.id });
        });
        
        return { postUrl };
    },
};
</script>

Vue 2 Support

ZiggyVue automatically detects Vue 2 and registers route() as a global method:
<template>
    <div>
        <a :href="route('posts.index')">All Posts</a>
    </div>
</template>

<script>
export default {
    mounted() {
        // Access route() via this in Vue 2
        console.log(this.route('posts.index'));
    },
};
</script>

Complete Examples

<template>
    <header class="site-header">
        <nav>
            <router-link 
                v-for="item in navigation" 
                :key="item.name"
                :to="item.path"
            >
                {{ item.label }}
            </router-link>
        </nav>
    </header>
</template>

<script setup>
import { inject, computed } from 'vue';

const route = inject('route');

const navigation = computed(() => [
    { name: 'home', label: 'Home', path: route('home') },
    { name: 'about', label: 'About', path: route('about') },
    { name: 'contact', label: 'Contact', path: route('contact') },
]);
</script>

Form Submission

<template>
    <form @submit.prevent="handleSubmit">
        <input v-model="post.title" type="text" required />
        <textarea v-model="post.body" required></textarea>
        <button type="submit">{{ isEditing ? 'Update' : 'Create' }}</button>
    </form>
</template>

<script setup>
import { inject, ref, computed } from 'vue';
import axios from 'axios';

const route = inject('route');
const props = defineProps({
    post: Object,
});

const isEditing = computed(() => !!props.post?.id);

const handleSubmit = async () => {
    const url = isEditing.value
        ? route('posts.update', { post: props.post.id })
        : route('posts.store');
    
    const method = isEditing.value ? 'put' : 'post';
    
    await axios[method](url, props.post);
};
</script>

Data Fetching with Composables

<template>
    <div>
        <div v-if="loading">Loading...</div>
        <div v-else-if="error">Error: {{ error }}</div>
        <div v-else>
            <h1>{{ post.title }}</h1>
            <p>{{ post.body }}</p>
        </div>
    </div>
</template>

<script setup>
import { inject, ref, onMounted } from 'vue';

const route = inject('route');
const props = defineProps(['postId']);

const post = ref(null);
const loading = ref(true);
const error = ref(null);

const fetchPost = async () => {
    try {
        loading.value = true;
        const response = await fetch(route('api.posts.show', { post: props.postId }));
        post.value = await response.json();
    } catch (e) {
        error.value = e.message;
    } finally {
        loading.value = false;
    }
};

onMounted(fetchPost);
</script>

TypeScript Support

To use Ziggy with TypeScript in Vue, you need to declare the route property on Vue’s component interface.

Vue 3 TypeScript Declaration

Create a ziggy.d.ts file in your project:
// ziggy.d.ts
import { route as routeFn } from 'ziggy-js';

declare module 'vue' {
    interface ComponentCustomProperties {
        route: typeof routeFn;
    }
}

export {};
This enables TypeScript to recognize route() in your templates and Options API components without type errors.

Using with <script setup lang="ts">

<template>
    <a :href="route('posts.show', { post: props.post })" class="post-link">
        {{ post.title }}
    </a>
</template>

<script setup lang="ts">
import { inject } from 'vue';
import { route as routeFn } from 'ziggy-js';

interface Post {
    id: number;
    title: string;
    slug: string;
}

const route = inject<typeof routeFn>('route')!;
const props = defineProps<{
    post: Post;
}>();
</script>

Typed Route Parameters

For full type safety with route names and parameters, generate Ziggy’s type definitions:
php artisan ziggy:generate --types
Then your routes will have full autocomplete:
<script setup lang="ts">
import { inject } from 'vue';
import type { route as routeFn } from 'ziggy-js';

const route = inject<typeof routeFn>('route')!;

// TypeScript knows 'posts.show' exists and requires a 'post' parameter
const url = route('posts.show', { post: 1 });
</script>

Plugin Implementation

The ZiggyVue plugin is implemented as follows:
export const ZiggyVue = {
    install(app, options) {
        const r = (name, params, absolute, config = options) =>
            route(name, params, absolute, config);

        if (parseInt(app.version) > 2) {
            // Vue 3: Add to global properties and provide for inject()
            app.config.globalProperties.route = r;
            app.provide('route', r);
        } else {
            // Vue 2: Add as a mixin method
            app.mixin({
                methods: {
                    route: r,
                },
            });
        }
    },
};
The plugin automatically passes the config you provide to .use(ZiggyVue, config) to every route() call, so you don’t need to pass it manually.

Troubleshooting

route is not defined in templates

Ensure you’ve installed the ZiggyVue plugin:
import { ZiggyVue } from 'ziggy-js';
app.use(ZiggyVue);

inject() returns undefined

Make sure the plugin is installed before mounting your app, and that you’re using Vue 3.

TypeScript errors in templates

Add the module declaration for ComponentCustomProperties as shown in the TypeScript section above.

Next Steps

TypeScript

Set up full type safety and autocompletion

React

Use Ziggy with React instead

Route Helper

Learn all route() function capabilities

JavaScript Frameworks

General framework integration guide

Build docs developers (and LLMs) love