mrshanebarron/skeleton
A Laravel package skeleton offering a clean starting point for building reusable packages, with standard structure, service provider setup, and sensible defaults for testing, configuration, and distribution.
First, orient yourself on what this package actually is. The repo is described as a "Laravel UI component package," but its own description frames it as a package scaffold — a starting point for building reusable Laravel packages. Those are two different jobs, and your first step is to decide which one you're doing:
composer require mrshanebarron/skeleton, publish config, use whatever components/commands it exposes.If you're consuming it, don't assume a documented public API exists. Read src/ before you write anything against it.
Minimal install (consumer path):
composer require mrshanebarron/skeletonbootstrap/providers.php; older apps need the provider in config/app.php. If you see no provider registered, that's your first bug.php artisan vendor:publish --provider="MrShanebarron\Skeleton\SkeletonServiceProvider" --list
Use --list first — you do not want to blind-publish migrations and assets into the repo. On v1.0.1 the config file is sb--prefixed (config/sb-skeleton.php or similar); on v1.0.0 it was not.php artisan tinker
>>> config('sb-skeleton')
Minimal scaffold (author path):
composer.json, src/, the service provider class name, tests).composer install and composer dump-autoload; confirm PSR-4 maps to your namespace.php artisan test (or vendor/bin/phpunit) — the basic test setup is part of the value here.Where to look first, in order:
composer.json — the actual package name, PSR-4 root, and Laravel/PHP constraints. This is your compatibility truth; ignore the description.register() / boot() — mergeConfigFrom(), publishes(), container bindings, view/component registration.config/sb-*.php — the only real knob surface.src/ — if it's genuinely a UI package, look for View/Components/ and Blade views under resources/views/.tests/ — the skeleton's tests double as usage documentation.First use case: publish the config, set your .env-backed values into config/sb-skeleton.php, then render the package's component or resolve its class in tinker to confirm it boots. That's it — a 15-minute sanity loop.
Config-first, always. Drive the package from config/sb-*.php, never from hardcoded values or direct env() calls in your app code. Keep secrets in .env and reference them from config:
// config/sb-skeleton.php
'api_key' => env('SB_API_KEY'),
This is the only pattern that survives php artisan config:cache.
Commit your published config. Publish once, commit, and merge new keys by hand on upgrade. Re-publishing on deploy with --force will silently discard your customisations, and mergeConfigFrom() is shallow — new nested keys added by a release won't appear in your published file until you diff it.
Per-environment and per-tenant overrides. For staging/prod divergence, set config in your own provider before the package resolves its singleton:
// AppServiceProvider::register()
$this->app['config']->set('sb-skeleton.driver', 'redis');
For per-request or multi-tenant overrides, set → resolve → use → reset, or bind a fresh instance. Do not leave mutated config lying around; queue workers and Octane will carry it into the next request.
UI component usage (if that's what you're here for):
<x-skeleton::button variant="primary">Save</x-skeleton::button>
Publish views only if you need to restyle: vendor:publish --tag=views. Otherwise rely on the package's shipped Blade and override via your own component wrapper — that survives upgrades cleanly.
Testing patterns:
config()->set('sb-skeleton.mode', 'fake');
$this->blade('<x-skeleton::foo />')) and assert the rendered output, not internal state.Deploy workflow. Put these in order in your deploy script:
php artisan config:clear
# only if you don't keep local customisations:
# php artisan vendor:publish --tag=config --force
php artisan config:cache
php artisan queue:restart # if workers hold config
php artisan octane:reload # if using Octane
Extension points worth using, in preference order:
If you're authoring from the skeleton: treat it as a template, not a dependency. Vendor it as a git template or run composer create-project, then immediately rename the namespace and delete sample artifacts so nothing collides with the real package later.
The v1.0.1 config rename is the number-one trap. The config file is now sb--prefixed. Consequences for anyone who installed v1.0.0:
config('sb-skeleton.…') falls back to package defaults silently — no error, no deprecation warning.composer update" or "why is it using defaults in prod?"config:clear, diff old file against vendor/mrshanebarron/skeleton/config/sb-*.php, port your keys, delete the old file, then config:cache..env comments, and deploy scripts for references to the old key.config:cache hides .env. Any package code (or yours) calling env() directly instead of config() will return null in production. This is the classic "works locally, broken on the server" cause. If you're authoring a package from this skeleton, this is the single most important rule to enforce in review.
Singletons capture config at resolve time. Changing config after the package's singleton is built does nothing. Call app()->forgetInstance(SomeClass::class) first, or bind a new instance.
--force overwrites, it doesn't merge. Back up customised published files before any vendor:publish --force. Better: don't publish-and-customise at all — override in your own provider.
Untagged vendor:publish pulls everything. It will drag migrations, assets, and views into your repo. Always use --tag. If you don't know the tag, --list it.
Octane and queue workers hold config in memory. After any config change deployment, php artisan queue:restart and php artisan octane:reload. Without this, you'll spend an hour debugging a change that's already live.
Debugging one-liners:
php artisan tinker --execute="dump(config('sb-skeleton'));" # merged view
php artisan config:show sb-skeleton # Laravel 9+
php artisan about # confirm no stale config cache
composer show mrshanebarron/skeleton # actual installed version
Release-hygiene signal. A user-visible filename reaching a 1.0.0 tag is a QA gap on the publish path. Expect further 1.0.x churn — pin ^1.0.1 rather than ^1.0, and read changelogs on every bump rather than auto-merging.
Add a deploy-time assertion. The rename failure is silent; make it loud. Two lines in a provider's boot() or a deploy check:
abort_unless(config('sb-skeleton.api_key'), 500, 'sb-skeleton config not loaded — check for orphaned config file.');
Scaffold-specific pitfall: if you copy the skeleton as a starting point, the sb- config prefix and the sample namespace will follow you. Rename both in the same commit or you'll end up with two packages writing to the same config path.
Zero stars, single maintainer. No community to lean on. Budget for fork-and-own cost if you build something load-bearing on it, and keep your usage surface thin — config reads and published views, not subclassed internals.
How can I help you explore Laravel packages today?