Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Character Counter Laravel Package

schmeits/filament-character-counter

Easy drop-in Filament character counter for TextField, Textarea and RichEditor fields in Filament v4 and v5. Add a characterLimit or maxLength to display a live character count, optionally shown inside the control. Translation strings are publishable, and v3.2 is supported via 1.x.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

This is a narrowly scoped, single-responsibility package: it contributes form-field components that extend Filament's own TextInput, Textarea and RichEditor to render a live character counter. It is not a plugin/panel provider in the usual Filament sense — there is no service provider registration in the README, no panel configuration, and no runtime hook into the request lifecycle beyond the form component itself.

  • Surface area is small and well-bounded: one trait (HasCharacterLimit), three field subclasses, a handful of Blade views, a CSS file, and translation files.
  • Dependency footprint is favourable as of v5.2.0: the package requires filament/forms and filament/support rather than filament/filament. That is the right call for a leaf package and means adoption does not pull a panel package into contexts that don't need it.
  • The integration model is the main architectural consideration: usage is via Schmeits\FilamentCharacterCounter\Forms\Components\TextInput — i.e. developers swap their field imports. This is an import-level coupling, not a configuration-level one. If the package is later abandoned, removal is a mechanical find-and-replace, which keeps the exit cost low, but it does mean vendor-specific imports will proliferate across every resource that uses it unless you standardise on an internal wrapper.
  • It composes with Filament rather than replacing it (v5.2.0 explicitly stopped shadowing the RichEditor view so Filament renders the editor itself). That is the architecturally correct direction and materially reduces the blast radius compared with earlier 5.x behaviour.

Integration feasibility

Feasible in an afternoon. composer require schmeits/filament-character-counter:"^5.2", optionally publish translations, then adopt per field.

The version matrix is the one point of confusion a TPM should resolve up front, because the README and the changelog disagree in emphasis:

Package Filament PHP Notes
1.x 3.2 >8.1 Legacy, out of scope
4.x 4.x >8.2 Superseded by 5.x
5.x 4.x – 5.x >8.2 Single line supports both majors

The v5.2.0 notes add that Laravel 11 is no longer tested (Composer refuses those releases due to open advisories) and CI covers PHP 8.2–8.5 on Laravel 12 and 13. The README's PHP column has not been updated to reflect 8.3–8.5 coverage. Practically: standardise on ^5.2 and do not adopt the 4.x line.

Technical risk

Risk is low in absolute terms but non-zero, and clustered in three places:

  1. RichEditor counting is DOM-coupled. v5.2.0 counts via $el.querySelector('.fi-fo-rich-editor-content'), a Filament-internal CSS class. This is a pragmatic and defensible choice (the TipTap instance isn't reliably reachable), but it is a private contract. A Filament minor/patch that renames or restructures that class silently degrades the counter — likely to "always zero" or "never updates" rather than to a hard error, which is the worst failure shape: silent and only noticed by users.
  2. Release history shows semantic churn on the core API. characterLimit() was made a hard limit, then reverted to a soft limit in v5.0.1 (#36). The changelog also carries duplicated "Fixed" blocks and a stray Fixed.md, indicating release hygiene is inconsistent. A soft limit is the correct design, but anyone who adopted during the hard-limit window may hold the wrong mental model — and any documentation you write internally must state explicitly that characterLimit() does not enforce anything.
  3. Prior shadowing regression. v5.2.0 documents that earlier 5.x views silently disabled file attachments, mentions, floating toolbars, link protocols and resizable images in the RichEditor. This is fixed, but it is the characteristic failure mode of a package that extends a framework component, and it argues for an explicit verification step rather than a "it renders, ship it" acceptance.

Secondary risks: bus factor of one (single maintainer, 34 stars, 0 dependents — no ecosystem validation), and the removal of the character_seperator translation key in v5.2.0, which will quietly orphan any previously published translation override.

Key questions

  • Does characterLimit() need to enforce, or only inform? If enforcement matters, we pair it with maxLength(), a Laravel validation rule, and a correct DB column width — the package must not be the enforcement layer.
  • Does the browser-side count agree with server-side validation for the content we actually collect? v5.2.0 counts per code point to match Laravel's max:, but ZWJ emoji and combining sequences still diverge from user-perceived length. Decide which definition we promise.
  • Do we lose any RichEditor capability (mentions, attachments, floating toolbars, custom toolbars) on our Filament version? Needs a rendered check on our own panel, not a README assertion.
  • Is accessibility acceptable? v5.2.0 added aria-describedby, a spoken exceeded message and non-colour-only signalling ("(12 too many)"). Verify with our own screen-reader pass before claiming compliance.
  • Which Filament major are we on, and does our upgrade cadence let us keep this package's major aligned? The field classes extend Filament's, so a Filament major bump is a forced package major bump.
  • Do we standardise on the package's field classes, or wrap them behind our own application-level components?

Integration Approach

Stack fit

Clean fit for a modern Filament application on Laravel 12/13, PHP 8.2+, Filament 4 or 5 (Livewire 3 or 4). No database footprint, no queues, no scheduled work, no external services, no configuration file. It is a presentation-layer addition only, which is why the stack-fit question is really a version-alignment question rather than a capability question.

It is a poor fit for anything not on Filament forms (plain Blade, Livewire-only forms, Nova, Inertia), and irrelevant for Filament 3.2 unless pinned to 1.x, which should be treated as end-of-life for planning purposes.

Migration path

There is no data migration and no schema change. The migration is import hygiene:

  1. Add the dependency pinned to ^5.2 (do not float to *, and do not accept 4.x).
  2. Publish translations only if we need to override copy; otherwise rely on shipped locales and skip the publish step (fewer files to maintain, and avoids stale keys like the removed character_seperator).
  3. For each adopting field, replace the Filament import with the package import in that file only.
  4. Rollback is reverting imports — no state to unwind. Document this so the exit cost stays visible and cheap.

If we want to avoid vendor imports spread across dozens of resources, wrap once: define App\Forms\Components\TextInput extends Schmeits\...\TextInput and import ours everywhere. That reduces a future swap to a single file, at the cost of a thin indirection layer. For a codebase likely to adopt this on more than a handful of fields, the wrapper is worth it; for a pilot, it is premature.

Compatibility

  • Pin ^5.2. Anything earlier in the 5.x line carries the RichEditor shadowing regression and the polling/hook leak.
  • Tie the package major to the Filament major in our upgrade runbook. Filament 4 → 5 stays on 5.x, but a future Filament 6 will require an explicit package decision.
  • Do not treat the README matrix as authoritative; the changelog and CI matrix are more current. Record the verified combination (Filament version, Livewire version, PHP version, package version) in our own notes.
  • Laravel 11 is not a supported target for 5.2+ releases.

Sequencing

  1. Spike (½ day). Install in a branch, apply to one TextInput and one Textarea in a low-traffic admin resource. Confirm counter renders, updates on paste/autofill/$set(), and respects maxLength.
  2. RichEditor verification gate. Apply to a single RichEditor that uses our real toolbar configuration. Explicitly confirm attachments, mentions, floating toolbars and link protocols still work, and that the counter tracks typing and paste. This is the acceptance gate; do not proceed past it on README evidence alone.
  3. Accessibility check. Screen-reader pass on one field in both normal and over-limit states; confirm the over-limit message is announced and not conveyed by colour alone.
  4. Convention decision. Either adopt vendor imports as the standard or introduce the internal wrapper — decide once, before widening, so we don't end up with both.
  5. Widen to remaining resources, prioritising fields where a visible limit genuinely reduces user error (SEO/meta descriptions, slugs, short bios, SMS-style copy).
  6. Guardrails. Add a validation rule and DB column width wherever a hard limit is contractually required, and a smoke test asserting the RichEditor toolbar still renders the capabilities we depend on.

Operational Impact

Maintenance

Ongoing maintenance is close to zero by design: no migrations, no config, no runtime services, plain CSS with Filament colour variables (no build step as of v5.2.0). The real maintenance cost is upgrade watching — we must review each release rather than blind-bumping, because this package's history includes a semantic API change (characterLimit() hard → soft) and a regression class (RichEditor feature shadowing) that would not surface in a green test suite. Budget a short review per Filament minor upgrade plus one per package release.

Governance risk is the bus factor: one maintainer, 34 stars, 0 dependents, no commercial backing. Mitigation is cheap — the package is small enough that forking and owning it internally is a realistic fallback (days, not weeks), and the MIT licence permits it. The import-level integration means even a full abandonment is a find-and-replace, not a rewrite. That asymmetry (low value, but also low exit cost) is what makes adoption defensible.

Support

Community-only, MIT, no SLA, no vendor contact. Issue #36 and #50 in the changelog show the maintainer is responsive and that fixes ship. The changelog, however, has duplicated blocks and a stray Fixed.md, so it is a signal of intent rather than a reliable audit trail — verify behaviour empirically rather than by reading release notes. Our internal documentation should state the verified version matrix and the soft-limit semantics; don't rely on the README, which still advertises a PHP floor that the release notes have moved past.

Scaling

Effectively free. Counting is client-side Alpine/DOM work with no queries, no server round-trips, no cache, no storage. It scales with the browser, not with the backend. The one historical scaling concern — a 500 ms polling loop and a leaked Livewire hook per editor instance — was removed in v5.2.0, so on ^5.2 a form with many counters should not accumulate listeners. Worth a quick sanity check on a form with several RichEditors, but this is not a capacity-planning item.

Failure modes

Failure Likelihood Detection Impact Mitigation
RichEditor DOM selector changes upstream → counter freezes or reads 0 Medium over time Manual/visual only Silent; users lose the affordance they were promised Pin version, verify on Fibament upgrades, smoke test
Team assumes characterLimit() enforces; oversize data reaches DB Medium at onboarding Truncation error or silent cut Data loss or 500s Always pair with maxLength() + validation + column width; document soft-limit semantics
RichEditor capabilities silently disabled by a view override Low on ^5.2, was real earlier Manual toolbar check Feature regression invisible to tests RichEditor verification gate; smoke test on toolbar
Counter and server disagree on length (ZWJ emoji, combining marks) Low–medium User complaint at the boundary Confusing validation errors Define the counting rule we promise; test boundary cases
Package abandoned / stale on next Filament major Medium, longer horizon Release cadence review Forced fork or removal Keep imports centralised; wrapper if adoption widens
Stale published translation overrides after key removal Low Missing/incorrect copy Cosmetic Don't publish translations unless overriding; review on upgrade

Ramp-up

Near-zero for developers: one composer require, one import swap, one fluent method. Expect ~15 minutes to first working counter and ~1 hour to cover a resource set with conventions agreed. The ramp-up cost that matters is organisational, not technical: making sure every developer knows characterLimit() informs and maxLength() enforces, and that the RichEditor check is a release gate rather than an assumption. That is a short paragraph in the team's form-field conventions doc, and it is the highest-leverage artefact this adoption produces.

Recommendation. Adopt on ^5.2 for TextInput and Textarea, where the value (visible limits reduce user error) clearly exceeds the near-zero risk and the exit cost is a find-and-replace. Treat RichEditor as conditional: adopt only after the explicit capability verification passes on our Filament version, since that is where the package's coupling to framework internals is real and where its own history shows the only materially damaging regression. Keep the package out of the enforcement path entirely — it is a UX affordance, and the schema and validation rules remain the source of truth.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib