The Problem Every Package Developer Knows
Ask any Laravel developer who maintains internal or private packages about their development workflow and you will hear some version of the same story.
You are working on company/crm-package which needs to be tested inside app-one. The official workflow is:
- Edit
composer.jsoninapp-oneto add a"repositories"entry pointing to your local package directory - Run
composer update company/crm-package - Make a change in the package
- Either run
composer update company/crm-packageagain, or clear the Composer class cache, or restart your queue worker, or some combination of all three - Repeat
In a monorepo with five packages that reference each other, this becomes a genuinely painful workflow that consumes significant development time. I measured it once: between Composer invocations, cache-busting steps, and debugging phantom "old code still running" issues, the overhead was roughly 20 minutes per hour of actual package development.
LaraLink is the tool I built to eliminate that overhead.
How LaraLink Works
LaraLink uses Composer's path repository type to create a symlinked connection between your local package directory and your application. The difference from doing this manually is that LaraLink:
- Handles the
composer.jsonmodification for you - Tracks all links in a local
.laralinkregistry file - Ensures the autoloader is reloaded correctly after linking
- Provides a clean CLI to manage all your linked packages at once
- Removes all traces when you unlink
your-app/
├── composer.json ← LaraLink modifies this
├── .laralink ← LaraLink creates and manages this
└── vendor/
└── company/
└── crm-package -> ../../packages/crm-package ← Symlink created by LaraLink
Installation
composer require amjadiqbal/laralink --dev
php artisan laralink:install
The --dev flag ensures LaraLink itself does not end up in production deployments.
Linking a Package
# Link a package from a relative path
php artisan laralink:link ../packages/my-filament-widget
# Or an absolute path
php artisan laralink:link /Users/amjad/code/packages/my-filament-widget
LaraLink reads the package's composer.json to extract the package name and namespace, then:
- Adds a
pathrepository to your application'scomposer.json - Adds the package as a
require-devdependency - Runs
composer updateto create the symlink - Records the link in
.laralink
After linking, changes to the package source are reflected immediately in your application without any Composer or cache intervention.
Checking Your Links
php artisan laralink:status
Output:
┌─────────────────────────────────────────────────────────┐
│ LaraLink Status │
├──────────────────────────┬──────────────────────────────┤
│ Package │ Path │
├──────────────────────────┼──────────────────────────────┤
│ company/crm-package │ ../packages/crm-package │
│ company/email-templates │ ../packages/email-templates │
└──────────────────────────┴──────────────────────────────┘
2 packages linked
Unlinking When Done
When you are ready to switch back to the Packagist version:
php artisan laralink:unlink company/crm-package
LaraLink:
- Removes the
pathrepository fromcomposer.json - Changes the requirement from the linked version back to a semver constraint
- Runs
composer updateto restore the installed package from Packagist - Removes the record from
.laralink
The .laralink Registry File
{
"version": 1,
"links": {
"company/crm-package": {
"path": "../packages/crm-package",
"linked_at": "2026-03-10T14:32:00Z"
},
"company/email-templates": {
"path": "../packages/email-templates",
"linked_at": "2026-03-11T09:15:00Z"
}
}
}
This file should be added to .gitignore — it is a local developer state file, not application configuration.
Monorepo Support
LaraLink is particularly useful in monorepo setups. If you have a repository structure like:
workspace/
├── apps/
│ ├── app-one/
│ └── app-two/
└── packages/
├── crm-package/
├── notifications/
└── billing/
You can link all packages to an application in one pass:
cd apps/app-one
php artisan laralink:link-all ../../packages/
LaraLink will discover all directories in the target path that contain a valid composer.json and link them all.
Why Not Just Use Composer Path Repositories Directly?
You can. LaraLink is a convenience tool, not a technical breakthrough. The value is:
- No manual
composer.jsonediting — error-prone in complex files with many dependencies - Centralised state —
.laralinktells you exactly what is linked without readingcomposer.json - Clean unlink — restoring
composer.jsonto its pre-link state is non-trivial manually - Team conventions — the Artisan CLI makes the workflow consistent across team members
GitHub Repository
Source code and CLI documentation on GitHub.
composer require amjadiqbal/laralink --dev
Conclusion
LaraLink will not change your life. But if you develop Laravel packages regularly, it will quietly eliminate a small but consistently annoying source of friction. Sometimes the best tools are the ones you stop thinking about because they just work.