smorken/components
smorken/components is a Laravel package that provides reusable Blade components for common UI elements, helping you build consistent forms and interfaces with less repetitive markup. Install via Composer and use the components directly in your Blade views.
Prerequisites. A Laravel app with Composer, plus Bitbucket read access for every developer and every CI runner. This package is not on Packagist, so "install" is really "add a VCS repository and authenticate."
1. Add the repository and pin it. Never dev-master in a shared branch — pin a commit SHA so a force-push upstream cannot become a production incident:
"repositories": [
{
"type": "vcs",
"url": "[email protected]:scott_morken/components.git",
"no-api": true
}
],
"require": {
"smorken/components": "dev-master#abc1234"
}
no-api: true with the SSH URL avoids Composer's Bitbucket API round-trip and the app-password dance in CI. If you must use HTTPS, configure http-basic.bitbucket.org with a repo-scoped app password in the runner's environment.
2. Verify it resolves from a cold cache before writing any Blade:
composer clear-cache && composer install --no-scripts && composer show smorken/components
If that fails, nothing downstream matters. This step is the real go/no-go gate.
3. Read three files before using a single component. The package ships no README worth trusting; the source is the documentation:
vendor/smorken/components/composer.json — the illuminate/* and PHP constraints (this tells you whether your next Laravel upgrade is blocked) and the extra.laravel.providers entry (auto-discovery vs. manual registration).src/*ServiceProvider.php — look for loadViewsFrom, Blade::component, or Blade::componentNamespace. That single line tells you the exact tag prefix (x-smorken::, x-components::, or unprefixed).resources/views/ — the actual component inventory. Anonymous components map 1:1 to file paths; class components map to src/View/Components/.4. Publish, then confirm the namespace string:
php artisan vendor:publish --provider="Smorken\Components\ComponentsServiceProvider"
Use the FQCN you read from the manifest, not a guess. Publishing is what makes the package editable — and therefore ownable — so do it on day one rather than treating vendor/ as a black box.
5. First use case: one leaf component in a throwaway view. Pick the simplest thing in the inventory — a button, badge, or alert. Render it with a slot and an extra attribute, confirm the markup and that the attribute survived, then stop. Do not convert a whole page in the first sitting; you need to know how attribute forwarding behaves before you commit any layout to it.
Adapter layer, not direct usage. This is the single most important pattern. Never reference the package's tags from feature views. Wrap each component you adopt in your own:
{{-- resources/views/components/ui/button.blade.php --}}
@props(['variant' => 'primary'])
<x-smorken::button
{{ $attributes->class(["btn-{$variant}"]) }}
variant="{{ $variant }}"
>
{{ $slot }}
</x-smorken::button>
Adoption becomes a one-file change per component. Removal becomes rm -rf resources/views/components/ui/ and a search for <x-ui::. Without this wrapper you are doing a codebase-wide search-and-replace when the package is abandoned — which, given the maintenance signals, is a when, not an if.
Inventory-first workflow. Before authoring anything, produce docs/components.md: tag name, constructor params / @props, slots, and a one-line screenshot or markup sample. The team will otherwise re-read vendor/ every time they need a card. Ten minutes of documentation removes the main day-to-day cost of this dependency.
Attribute passthrough testing. Components that pass {{ $attributes }} on the root work fine; components that hardcode class and also spread $attributes are where you lose id, wire:model, data-*, and aria-*. Verify once per component:
<x-smorken::button id="x" class="mine" data-track="y" wire:click="save">Go</x-smorken::button>
If wire:click or data-track does not reach the DOM, that component needs a wrapper that re-applies them, or it does not get used on Livewire-heavy pages at all.
Class merging discipline. Laravel concatenates, it does not deduplicate. $attributes->merge(['class' => 'btn']) against a component that hardcodes btn produces btn btn. Harmless until you need a variant override; then CSS specificity decides the winner, not source order. Where variants matter, drive them through a config map (published config, or your own config/ui.php) rather than editing the package's Blade files directly.
Slot contract check. Confirm whether the component expects default slot, named slots (<x-slot:title>), or both, and whether it falls back gracefully when a named slot is absent. Components written without @isset($title) guards render empty <div>s and produce phantom padding that looks like a CSS bug.
Pin-and-review upgrades. composer update smorken/components should always be a manual, reviewed commit — change the SHA in composer.json, run the app, diff the published views. There is no semver contract here; a dev-master bump can rewrite every template.
Internal mirror on day one. Push the bitbucket repo to your own Git host and point the repositories entry at the mirror. The mirror is the artifact CI depends on; upstream becomes an optional patch source. This converts the highest-probability failure mode (repo deleted, renamed, made private, credentials rotated) from an outage into a non-event.
Smoke test every adopted component. A single Pest/PHPUnit test that renders a view containing all adopted components and asserts on the output catches Blazor namespace mistakes and Laravel-major upgrade breakage for a couple of lines of code:
it('renders the ui kit', function () {
$this->blade('<x-ui::button>Go</x-ui::button>')->assertSee('Go');
});
You will get more regression coverage from this than the package likely ships.
On Laravel 7+ you probably should not adopt this at all. Native anonymous components (resources/views/components/*.blade.php) and class components give you exactly this capability for free, with no VCS repository, no Bitbucket credentials, no bus factor, and no upgrade deadlock. The package's only genuine asset is its hand-authored markup — the styling decisions, not the rendering mechanism. If the markup is good, harvest the Blade files into your own components/ directory and drop the dependency. That is the correct outcome more often than an install is.
Zero stars, zero dependents, single maintainer. There is no community to catch regressions, no advisory coverage, and no release cadence to plan against. composer audit will not warn you about anything inside it. Assign a named internal owner before it enters the lockfile — this will not pass a procurement review without one.
Check the license before you get attached. Personal Bitbucket repos frequently ship without one. No license means no clear right to use or redistribute in a corporate setting, regardless of what the code does.
Bitbucket is a build-time single point of failure. Any workflow that reaches out to Bitbucket during composer install — a fresh CI runner, a new laptop, a docker build — breaks the moment the repo moves or credentials rotate. The mirror is not optional hardening; it is the mitigation for the most likely failure.
illuminate/* constraints cap your Laravel upgrade. Read them from vendor/smorken/components/composer.json on day one. If the package caps below your target major, you have two choices at upgrade time: fork and port, or remove it under schedule pressure. Neither is good, and knowing which three months early is the whole value of reading that file now.
Component name collisions. If the service provider calls Blade::component('button', ...) without a prefix, it shadows your <x-button>. Grep the provider before assuming your own components still resolve. The failure is silent — your markup is replaced, not error-flagged.
Renders empty? Debug in this order: php artisan view:clear → confirm the published path is resources/views/vendor/{namespace}/ and that {namespace} matches the loadViewsFrom argument exactly → check whether the provider registered a namespace at all (unprefixed use of a namespaced component renders nothing, no exception). php artisan view:cache in CI will surface missing views earlier than a browser will.
Published assets bypass Vite. If the package ships CSS/JS into public/vendor/, that file is outside your build graph: no tree-shaking, no hashing, no purging. Either import it in vite.config.js or delete it and replicate the styles in your own stylesheet. Measure the bundle delta during the pilot, not after.
Publish views rather than subclassing. Once you are editing the published Blade files more than occasionally, you have effectively forked. Say so, move the repo to your own namespace, and stop pretending it is a dependency. The uncomfortable middle — half-customized vendor views with an unowned upstream — is the state that costs the most and is hardest to leave.
How can I help you explore Laravel packages today?