naif/text-with-icon
A lightweight Laravel package for pairing text with icons, helping you render consistent labeled UI elements across menus, buttons, and lists. Simple to integrate into Blade views with minimal setup.
Install
composer require naif/text-with-icon
The package is a Laravel Nova custom field. Nova auto-discovers the service provider; there is nothing to register in config/app.php.
Look here first
README.md — usually shows the exact FQCN and the icon API.src/ — confirm the field class (likely Naif\TextWithIcon\TextWithIcon) and how icon() is defined.composer.json — check the laravel/nova version constraint. This is the single most important line for a 5-star Nova package; small field packages are frequently pinned to Nova 2/3 and silently break on Nova 4/5.dist/js/field.js — if this file is committed, the field works out of the box. If only resources/js/ exists, you must build assets yourself.resources/js/components/Field.vue — tells you whether icon() expects a Heroicon name, a Font Awesome class, or a raw SVG string.First use case
Drop it into any Resource where a text input benefits from a visual cue — URLs, email addresses, phone numbers, external IDs:
use Naif\TextWithIcon\TextWithIcon;
public function fields(NovaRequest $request)
{
return [
TextWithIcon::make('Website')
->icon('globe')
->rules('nullable', 'url'),
TextWithIcon::make('Support Email')
->icon('mail')
->rules('nullable', 'email'),
];
}
Load /nova/resources/{resource}/{id}/edit and confirm the icon renders inline with the input. If the icon is missing but the input works, the Vue component isn't loading — see Gotchas.
Pure presentation layer. The field extends Nova's Text, so treat the icon as cosmetic metadata only. Keep the underlying database column a plain string; never store the icon identifier in the model. All standard Text methods (rules, nullable, placeholder, help, default, sortable, hideFromIndex) continue to work because the class inherits from Text.
Consistent icon language across a resource. Because the icon is set per-field, centralize the mapping so a form doesn't drift into mixed styles:
class IconText
{
public static function make(string $name, string $icon): TextWithIcon
{
return TextWithIcon::make($name)->icon($icon);
}
}
// usage
IconText::make('GitHub', 'github'),
IconText::make('LinkedIn', 'linkedin'),
Conditional icons via meta. If icon() only accepts a static value, push it through withMeta and read it in the component:
TextWithIcon::make('Endpoint')
->icon($this->isExternal() ? 'external-link' : 'link')
->withMeta(['iconColor' => $this->isExternal() ? 'text-orange-500' : 'text-gray-400']);
This works only if the bundled Vue component reads field.icon from the field meta — verify in Field.vue before relying on it.
Form/Index/Detail parity. Nova renders the same field component in different contexts. If the icon clutters the index table, use ->hideFromIndex() or ->onlyOnForms(). If you want the icon in the detail view but not the table, ->showOnDetail() is the right lever, not a fork of the component.
Pairing with Nova's built-in affordances. Combine with ->copyable() for IDs and tokens, ->suggest() for enums, and ->displayUsing() when you want the icon-bearing input to show a derived value on detail:
TextWithIcon::make('API Key')
->icon('key')
->copyable()
->readonly(fn () => ! $request->user()->isAdmin()),
Nova 4 field registration. If you need dynamic behavior, register a closure in NovaServiceProvider::fields() and resolve TextWithIcon there — this keeps the class name out of every Resource and lets you swap the implementation later without touching resources.
composer.json require block. If it says "laravel/nova": "^2.0|^3.0", Composer will install it on Nova 4 only if you relax constraints — and the Vue component may then fail to mount silently. Symptom: the field renders as a bare text input with no icon, no console error visible to the user.dist/js/field.js is absent, the package expects you to run npm install && npm run prod from inside the vendor directory using Nova's own webpack.mix.js. This is fragile across Composer updates. Vendor the built output or fork it.icon() contract. Small packages rarely validate the argument. Passing 'fa-user' when the component injects Heroicons yields an empty <svg>. Inspect Field.vue and, if it uses v-html, treat the icon string as untrusted — never feed it user input, since Nova's meta is serialized straight into the page.naif/text-with-icon and the GitHub repo name differ from the class namespace in many of these micro-packages. Always confirm with grep -r "class .* extends Text" vendor/naif/text-with-icon/src/.composer update, run php artisan nova:publish and hard-refresh the browser. Nova caches dist assets by hash; a stale manifest leaves you staring at the old component.Text plus a small Vue component wrapping <DefaultField> — so if the package doesn't fit your Nova version cleanly, reimplementing it in your app's nova-components/ directory is usually cheaper than debugging vendor code.component(), icon(), and inheritance from Text — nothing else. If you need reactivity (color changes, tooltips, conditional rendering), override component() and ship your own Vue component rather than patching vendor/.jsonSerialize() includes the expected icon meta key, so a Nova upgrade that drops the meta is caught in CI rather than in production.How can I help you explore Laravel packages today?