saumini/ellipsis-textarea
A Laravel form component that renders long text in a textarea with a tidy ellipsis, letting users expand and collapse the full content on demand. Keep your forms compact and readable without losing any text.
1. Check compatibility before installing. This is a Nova field package, so its usefulness is bounded by your Nova major version. Read composer.json in the repo and confirm the laravel/nova constraint matches yours (Nova 3 = Vue 2, Nova 4/5 = Vue 3 — a field built for one will not render in the other). With 11 stars and zero dependents, you are the QA department.
composer require saumini/ellipsis-textarea
Nova field packages normally auto-register via extra.laravel.providers in their composer.json. If the field does not appear in the browser, add the package's FieldServiceProvider to config/app.php manually and run php artisan nova:publish.
2. Look at two files first. The README is thin, so go straight to:
src/*.php — the field class, which tells you the real fluent API (grep -n "public function" vendor/saumini/ellipsis-textarea/src/*.php).resources/js/field.js — the Vue component, which tells you when truncation actually happens (index vs. detail vs. form) and which props control it.3. First use case. Replace a noisy Textarea in a Resource with the ellipsis variant so long-form content (excerpts, descriptions, notes, changelogs) stops blowing out the Index table:
use Saumini\EllipsisTextarea\EllipsisTextarea;
public function fields(NovaRequest $request)
{
return [
EllipsisTextarea::make('Excerpt', 'excerpt')
->rules('max:2000')
->hideFromIndex(), // full text elsewhere; see patterns below
];
}
Presentation-only field, full text in the DB. Treat this as a display concern. Never truncate on write — store the complete value and let the field clamp it. That keeps search, sorting, exports, and API responses intact.
Split index and detail. The cleanest pattern is two entries for the same column: a clamped field for the table and the untouched Textarea on detail.
EllipsisTextarea::make('Excerpt')->onlyOnIndex(),
Textarea::make('Excerpt')->onlyOnDetail()->rows(12),
This sidesteps the most common complaint about ellipsis widgets — a "show more" toggle that fights with Nova's own row height and tooltips.
Combine with Nova's standard modifiers. Because it extends Laravel\Nova\Fields\Textarea, ->help(), ->placeholder(), ->nullable(), ->rules(), ->sortable(), ->onlyOnPreview(), and ->dependsOn() should all compose normally. Validation is unaffected: rules run against the submitted value, not the rendered one.
Normalize before you truncate. If the underlying column stores HTML or Markdown from a rich editor, character-based slicing will cut mid-tag or leave dangling **. Strip first:
EllipsisTextarea::make('Body')
->resolveUsing(fn ($value) => Str::of($value)->stripTags()->squish()->value())
->onlyOnIndex(),
Server-side pre-trim for very long content. Truncation happens in the browser, so the full string still travels over the wire. For megabyte-scale columns, wrap with ->displayUsing(fn ($v) => Str::limit($v, 500)) so the payload shrinks before the ellipsis logic ever runs.
Consistency across surfaces. Apply the same field definition in Resources, Lenses, and any custom cards so a value is truncated identically everywhere a user encounters it.
src/ rather than guessing, and expect a BadMethodCallException to be your first clue.npm install in your app. A field that renders as a blank cell or throws "Failed to mount component: template or render function not defined" almost always means the service provider's boot() never ran or Nova::script() did not fire.app/Nova/Fields/ in an afternoon, which is often safer than depending on it. Do that if you need a bug fix.->canSee() or omit the field.->sortable() plus a tooltip (title attribute) exposing the full text.$resource->availableFields($request) contains the class, and snapshot the resolved value with ->resolve() to confirm your resolveUsing chain does not double-transform.<div> or a CSS line-clamp with no interactive element, keyboard users cannot expand it. Confirm the component emits a real <button>; if not, that is a five-line fork fix.How can I help you explore Laravel packages today?