wotz/filament-placeholder-input
wotz/filament-placeholder-input adds placeholder text support to Filament form inputs, helping Laravel developers improve form usability by displaying helpful hints inside fields. Lightweight, easy to install, and designed to integrate smoothly with existing Filament applications.
Architecture fit. Unchanged: the package remains a Filament form-field extension — a UI-layer component that lets an editor inject content (placeholders/merge tags) into a sibling field, most plausibly a Textarea, RichEditor, or MarkdownEditor. It adds no models, migrations, queues, or state-touching service providers. It sits at the outermost edge of a Filament panel and can be removed without residue, so its value is entirely UX ergonomics and a competent Filament developer could reproduce the behavior as a custom Field with a Blade view and a small Alpine component in roughly a day. Treat it as a convenience, not a capability.
One architectural detail is now confirmed and slightly changes the picture. The v2.1.1 fix — "Fix operator precedence in getVariables()" — proves the package contains a token-parsing layer (getVariables()), not just a view-layer insert button. That means it owns a small grammar for discovering/validating the placeholder syntax inside a field's value, and that grammar is a second compatibility surface alongside the Filament form-component contract. It is still presentation-adjacent (parsing a string), but it is logic we would inherit, and it is logic that recently shipped a correctness defect. The corollary remains: reproducible in-house, but the estimate should now include a parser, not just a Blade partial.
Integration feasibility. Mechanically trivial and unchanged: composer require wotzebra/filament-placeholder-input, then reference the field class in a form schema or register a panel plugin. No infrastructure, credentials, or external services. Realistic effort is still validation, not installation — confirming behavior inside repeaters, nested relation managers, and modal forms where Livewire keys and state paths are non-obvious — plus, now, validating getVariables() against the placeholder syntaxes we actually intend to store ({{x.y}}, {x.y}, [[x]], etc.). If our tokens don't match the parser's expectations, the field may insert text that it then fails to recognise.
Technical risk. Still high relative to value delivered, but the risk profile has shifted modestly and should be restated accurately:
@jyrkidn, not the author), dependabot PR #31, and a v2.1.0 → v2.1.1 changelog. This is direct evidence that (a) the repo has more than one release — the earlier "no releases since inception" concern was wrong, (b) there is a GitHub Actions workflow set (dependabot only operates on an active Actions/CI configuration), and (c) there is at least one outside contributor, which is a weak but real community signal. This does not make it safe; it makes the abandonment and hygiene concerns less severe than written.getVariables() is exactly the class of bug that produces silent wrong behavior rather than a crash: variables detected in one nesting/boolean combination and not another. It was fixed promptly and in a patch release, which is a good response, but it demonstrates the parser is young and under-exercised. Any adoption should include a small fixture table of the token shapes we intend to use.Key questions.
getVariables() recognises, and is a v2.x breaking change to that grammar plausible? Is getVariables() public API or internal?getVariables() also feed a resolution/render path? If it resolves, where does it render — and is that output escaped?RichEditor (Tiptap), Textarea, MarkdownEditor, or all three — and does it use the editor's public API or DOM manipulation?<script> or an event handler reach the rendered editor?Repeater, Builder, and RelationManager contexts?filament-tiptap-editor merge tags, documented custom-field recipe)?Verdict. Unchanged in direction, softened in tone. Do not adopt as a dependency of record, but the "one star, zero dependents, probably abandoned" framing no longer holds — this is an actively released 2.x package with an outside contributor and dependabot hygiene. The remaining blockers are the unverified CI matrix, the unverified XSS handling, and the newly visible youth of the parser. Either (1) spike it and, if the UX is right, vendor/fork it under our namespace with tests, a fixture table for our token shapes, and a Filament version pin, or (2) build the equivalent custom field in-house. Option 2 is still likely cheaper over a 12-month horizon, but the gap has narrowed enough that a one-day spike is now clearly worth running before committing to build.
Stack fit. Unchanged: native to Laravel 10/11 + Filament v3/v4 on Livewire/Alpine/Tailwind, Blade-rendered with Alpine interactivity, no React/Vue/Inertia involvement and no backend coupling. Nominal fit if the target surface is a Filament panel; irrelevant to any SPA or public-facing form.
Migration path. Still effectively zero-risk and additive at the view layer: no schema changes, no backfill, placeholders persist as ordinary text in an existing column. But the v2.1.1 fix sharpens the semantic decision flagged earlier. getVariables() implies the package has opinions about token syntax. Confirm:
getVariables() also power a resolution step? If resolution is ours, budget for the resolver separately — that is the real work and it is still not in this dependency.{{model.field}} convention before any content is authored. Retrofitting a grammar after editors have written hundreds of tokens is a content migration, not a config change.Compatibility. Pin the Filament constraint explicitly and treat any Filament upgrade as requiring re-test of this field. Verify PHP version support, Livewire 3 compatibility, and whether published views/assets collide with existing panel theming. Inspect the composer require block for loose constraints (^3.0 || ^4.0 with no matrix is a yellow flag). New: also pin the package itself to ^2.1 rather than ^2.0, since the parser changed within 2.x.
Sequencing.
getVariables() source — 30 minutes, and it may reorder everything below..github/workflows for a real test/lint matrix versus dependabot-only. Screenshot the evidence into the decision record.getVariables().Maintenance. Still the dominant cost and still disproportionate to the feature, but the trajectory has improved. An active 2.x line with dependabot and an outside contributor means we are less likely to inherit an orphan — while it also means upstream can move under us, which is its own maintenance burden: pinning is now mandatory, not advisory, and the parser is a second surface to re-validate. Assume we own the code on our timescale regardless: shallow fork, or accept that a Filament upgrade may silently break it. Budget a recurring line item for upgrade re-validation each Filament cycle, plus a one-off cost to build the token fixture table.
Support. GitHub issues only, no SLA, no security disclosure process, no LTS statement — unchanged. Mitigating and newly evidenced: a responsive maintainer (patch released for a real bug, PRs merged from a contributor). Bus factor is still effectively one. Keep the fork shallow and the diff reviewable so re-basing or replacing is a half-day task.
Scaling. Negligible and unchanged. Client-side and per-form-render; no query, queue, cache, or meaningful per-request overhead. getVariables() runs over a single field's string, so it is trivially cheap at any plausible admin-user count — worth a glance only if it is invoked on every keystroke against very large documents, which is a UX question, not a capacity one.
Failure modes.
getVariables() means the field can insert tokens it later fails to detect (or vice versa), producing silently inconsistent editor behavior. Detect with unit fixtures covering our token grammar, not manual QA.Ramp-up. Low and unchanged for anyone fluent in Filament custom fields — a few hours to read the source and published view. The ramp-up cost is not learning the package; it is learning Filament's form internals plus this package's small token grammar well enough to own the fork. Both are bounded and largely reusable. Net: cheap to try, cheap to replace, still expensive to depend on — but the "try" step is now more clearly worth the day.
How can I help you explore Laravel packages today?