herolabid / pathify
Use your Laravel routes in JavaScript - A powerful alternative to Ziggy
Requires
- php: ^8.2
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
README
Use your Laravel routes in JavaScript — A powerful alternative to Ziggy
Installation • Usage • Features • Frameworks • API
Why Pathify?
Pathify brings your Laravel routes to JavaScript with extras that Ziggy doesn't have:
┌─────────────────────────────────┬─────────┬─────────┐
│ Feature │ Ziggy │ Pathify │
├─────────────────────────────────┼─────────┼─────────┤
│ Route generation & parameters │ ✓ │ ✓ │
│ TypeScript support │ ✓ │ ✓ │
├─────────────────────────────────┼─────────┼─────────┤
│ Permission checking (can()) │ │ ✓ │
│ Minimal mode (reduce payload) │ │ ✓ │
│ Route caching │ │ ✓ │
│ Intelligent prefetching │ │ ✓ │
│ Auto breadcrumbs │ │ ✓ │
│ Navigation builder │ │ ✓ │
│ Multi-language URLs (i18n) │ │ ✓ │
│ Auto .gitignore management │ │ ✓ │
└─────────────────────────────────┴─────────┴─────────┘
Installation
# Install via Composer composer require herolabid/pathify # Publish config (optional) php artisan vendor:publish --tag=pathify-config # Publish JavaScript files (for Vue/React/TypeScript projects) php artisan vendor:publish --tag=pathify-js
This will copy JS/TS files to resources/js/vendor/pathify/.
Usage
Blade (Simplest)
Just add the directive — no imports needed:
<head> @pathify @vite(['resources/js/app.js']) </head>
// Available globally via window.Pathify route('posts.index') // → /posts route('posts.show', { post: 1 }) // → /posts/1
With Vue/React (Import Method)
After publishing JS files:
// Vue import { useRoute } from '@/vendor/pathify/vue' const route = useRoute() // React import { useRoute } from '@/vendor/pathify/react' const route = useRoute() // Vanilla JS import { route } from '@/vendor/pathify/route'
Inertia.js
import { setupPathifyFromPage, createPathifyListener } from '@/vendor/pathify/inertia' import { router } from '@inertiajs/vue3' // or @inertiajs/react createInertiaApp({ setup({ el, App, props, plugin }) { // Initialize BEFORE mounting setupPathifyFromPage(props.initialPage) // Keep in sync during navigation router.on('navigate', createPathifyListener()) // Mount app... }, })
Basic Examples
route('posts.index') // → /posts route('posts.show', { post: 1 }) // → /posts/1 route('posts.show', [1]) // → /posts/1 (positional) route('posts.index', { page: 2, sort: 'desc' }) // → /posts?page=2&sort=desc route.current() // → 'posts.show' route.current('posts.*') // → true route.has('posts.index') // → true route.params // → { post: '1' }
Features
Minimal Mode (NEW in v1.4.0)
Reduce payload by sending only routes relevant to current page:
// config/pathify.php 'minimal' => [ 'enabled' => true, 'include_current' => true, // users.show includes users.* 'include_common' => true, 'common_routes' => ['home', 'login', 'logout', 'dashboard'], 'always_include' => [], ],
Or via Blade directive:
{{-- Auto minimal (based on current route) --}} @pathifyMinimal {{-- Manual patterns --}} @pathify(['users.*', 'roles.*', 'dashboard'])
Result: If on users.edit, only sends ~10 routes instead of 50+!
Permission Checking
route.can('admin.dashboard') // → false (requires auth) route.can('posts.create') // → false (requires permission)
// Laravel route with permission Route::middleware(['auth', 'can:create-posts'])->group(function () { Route::get('/posts/create', [PostController::class, 'create'])->name('posts.create'); });
<a v-if="route.can('posts.create')" :href="route('posts.create')">New Post</a>
Route Filtering
// config/pathify.php 'only' => ['dashboard', 'users.*', 'roles.*'], // atau 'except' => ['admin.*', 'api.*', '_debugbar.*'],
Route Groups
// config/pathify.php 'groups' => [ 'public' => ['home', 'login', 'register'], 'admin' => ['users.*', 'roles.*'], ],
@pathify('admin') {{-- Only admin routes --}}
Breadcrumbs
route.breadcrumbs() // → [ // { name: 'home', label: 'Home', url: '/' }, // { name: 'posts.index', label: 'Posts', url: '/posts' }, // { name: 'posts.show', label: 'Show', url: '/posts/1' } // ]
Navigation Builder
// config/pathify.php 'navigation' => [ 'main' => [ ['route' => 'home', 'label' => 'Home'], ['route' => 'dashboard', 'label' => 'Dashboard', 'auth' => true], ['label' => 'Admin', 'permission' => 'admin', 'children' => [ ['route' => 'admin.users', 'label' => 'Users'], ]], ], ],
route.navigation('main') // Auto-filtered by user permissions
Localization
// config/pathify.php 'localization' => [ 'enabled' => true, 'locales' => ['en', 'id', 'es'], 'default' => 'en', ],
route('posts.index', { _locale: 'id' }) // → /id/posts route.t('posts.index', {}, 'es') // → /es/posts
Prefetching
// config/pathify.php 'prefetch' => [ 'enabled' => true, 'strategy' => 'hover', // 'hover', 'viewport', 'idle' ],
Caching
// config/pathify.php 'cache' => ['enabled' => env('PATHIFY_CACHE', false), 'ttl' => 3600],
php artisan pathify:clear
Frameworks
Vue
import { createApp } from 'vue' import { PathifyVue } from '@/vendor/pathify/vue' createApp(App).use(PathifyVue).mount('#app')
<script setup> import { useRoute } from '@/vendor/pathify/vue' const route = useRoute() </script> <template> <a :href="route('home')">Home</a> <span v-if="route.current('posts.*')">Viewing posts</span> </template>
React
import { useRoute } from '@/vendor/pathify/react' function Nav() { const route = useRoute() return <a href={route('home')}>Home</a> }
Inertia (Vue/React)
import { setupPathifyFromPage, createPathifyListener } from '@/vendor/pathify/inertia' import { router } from '@inertiajs/vue3' createInertiaApp({ setup({ el, App, props, plugin }) { setupPathifyFromPage(props.initialPage) router.on('navigate', createPathifyListener()) // ... }, })
TypeScript
Generate type definitions:
php artisan pathify:types
route('posts.show', { post: 1 }) // ✓ OK route('posts.show', {}) // ✗ Error: missing 'post'
API
JavaScript
route(name, params?, absolute?) // Generate URL route.current() // Get current route name route.current(name, params?) // Check if matches route.is(name, params?) // Alias for current() route.has(name) // Check existence (supports wildcard) route.can(name) // Check permission route.params // Current route parameters route.url // Base URL route.breadcrumbs(name?, params?) // Get breadcrumbs route.navigation(name) // Get nav items route.t(name, params?, locale?) // Localized URL
Blade Directives
@pathify {{-- All routes --}} @pathify('admin') {{-- Group mode --}} @pathify(['users.*']) {{-- Minimal mode with patterns --}} @pathifyMinimal {{-- Auto minimal mode --}} @pathifyMinimal(['users.*']) {{-- Manual minimal mode --}} @pathifyConfig {{-- Raw JSON config --}}
Artisan Commands
php artisan pathify:generate # Generate JS config file php artisan pathify:generate --types # With TypeScript definitions php artisan pathify:types # TypeScript definitions only php artisan pathify:clear # Clear route cache
Generated files are auto-added to .gitignore.
Troubleshooting
| Problem | Solution |
|---|---|
Pathify config not found |
Add @pathify directive or call setupPathifyFromPage() |
| Routes not appearing | Add name: ->name('posts.index'), check except config |
| TypeScript errors | Run php artisan pathify:types |
| Inertia not updating | Add router.on('navigate', createPathifyListener()) |
| Permissions not working | Set include_permissions: true in config |
Support
If you find this package helpful, consider buying me a coffee:
License
MIT - Built by Herolab ID