schmeits/filament-character-counter
composer require schmeits/filament-character-counter:"^5.0"
php artisan vendor:publish --tag="filament-character-counter-translations"
TextInput to a Filament form:
use Schmeits\FilamentCharacterCounter\Forms\Components\TextInput;
TextInput::make('title')
->characterLimit(50)
->rules(['max:50']),
src/Forms/Components/ for component implementations (e.g., TextInput.php, Textarea.php).// TextInput (e.g., for a product name)
TextInput::make('name')
->characterLimit(30)
->maxLength(30) // Hard limit + visual counter
->rules(['max:30']);
// Textarea (e.g., for a blog post excerpt)
Textarea::make('excerpt')
->characterLimit(160)
->rows(3);
use Filament\Forms\Components\RichEditor;
RichEditor::make('content')
->characterLimit(1000)
->columnSpanFull()
->disableToolbarButtons(['bold', 'italic']); // Optional: Customize toolbar
Textarea::make('bio')
->characterLimit(255)
->showCharacterCounter(fn (Get $get) => $get('is_public'))
->showInsideControl(true); // Place counter inside the field
TextInput::make('slug')
->characterLimit(fn (Get $get) => $get('is_short') ? 30 : 50)
->dehydrateStateUsing(fn (string $state) => Str::slug($state));
$form->afterStateUpdated(function (Form $form) {
if ($form->getComponent('title')->getStateLength() > 50) {
$form->addError('title', 'Exceeded character limit.');
}
});
protected function getListeners(): array
{
return [
'characterCountUpdated' => 'updateCharacterCount',
];
}
.filament-character-counter {
color: #ff4444;
font-size: 0.875rem;
}
characterLimit vs. maxLength:
characterLimit() is a soft limit (visual counter only).maxLength() enforces a hard limit (HTML maxlength + validation).TextInput::make('tagline')
->characterLimit(20)
->maxLength(20)
->rules(['max:20']);
RichEditor Quirks:
Livewire.hook('commit') for sync:
Livewire.hook('commit', () => {
updateCharacterCount();
});
->extraAttributes(['data-debounce' => '300'])
Translation Keys:
character_seperator (e.g., " / ") and character_label (e.g., "characters").config(['filament-character-counter.character_label' => 'zeichen']);
SPA/Alpine.js Issues:
@push('scripts')
<script src="{{ asset('js/alpine.js') }}" defer></script>
@endpush
Counter Not Updating?:
Alpine.start()).RichEditor Counting Incorrectly:
console.log(document.querySelector('.fi-fo-rich-editor-wrapper').textContent.length);
rich-editor.blade.php).Custom Counter Logic:
HasCharacterLimit trait to add logic (e.g., word limits):
use Schmeits\FilamentCharacterCounter\Concerns\HasCharacterLimit;
class CustomTextarea extends Textarea
{
use HasCharacterLimit;
public function wordLimit(int $limit): static
{
$this->extraAttributes(['data-word-limit' => $limit]);
return $this;
}
}
Localization:
character_label:
config([
'filament-character-counter' => [
'character_label' => trans('custom.characters'),
],
]);
Testing:
$field = TextInput::make('test')
->characterLimit(10);
$field->fill('test');
$this->assertEquals(4, $field->getStateLength());
How can I help you explore Laravel packages today?