lednerb/nova-text-copy-field
A Laravel Nova field that displays text with a one-click copy-to-clipboard button, making it easy to copy IDs, URLs, tokens, or other short values directly from Nova resources.
Install with Composer (the field auto-registers; there is no service provider to add and no config file to publish):
composer require sixlive/nova-text-copy-field
Add the field to a Nova resource. The first, and by far the most common, use case is a value users must copy exactly — API tokens, webhook secrets, UUIDs, order references:
use Sixlive\TextCopy\TextCopy;
public function fields(Request $request)
{
return [
TextCopy::make('API Token')
->onlyOnDetail()
->readonly(),
];
}
Where to look first:
Sixlive\TextCopy\TextCopy) — it extends Nova's Text field, so every Text modifier (rules, help, nullable, default, hideFromIndex, …) applies.resources/js — this is what renders the input plus the copy button, and it is where clipboard behaviour lives.composer.lock / composer show sixlive/nova-text-copy-field — confirm the installed version actually matches your Nova major (v1.6.0 aligns the icon set with Nova 4's Heroicons styling).Smoke test it on a throwaway resource field with a hard-coded ->default('abc123') before wiring it to a real token, so you separate "field doesn't render" from "value resolves to null".
Show-once secrets (the canonical pattern). Combine readonly() with onlyOnDetail() so the token is displayed but never editable or re-submitted, and pair it with a resolveUsing callback when the plaintext value only exists transiently:
TextCopy::make('Plain Text Token')
->onlyOnDetail()
->readonly()
->resolveUsing(fn () => session('plain_text_token'))
->help('Copy this now — it will not be shown again.'),
Because the field is readonly(), Nova keeps it out of the create/update payload, which protects you from accidentally overwriting a hashed column with the displayed string.
Copyable identifiers on detail screens. For internal tools, put references next to the record:
TextCopy::make('Stripe Customer ID', 'stripe_id')
->onlyOnDetail()
->readonly(),
Reserve TextCopy for values people actually paste elsewhere; for everything else a plain Text keeps the UI quieter.
Editable copy fields. On create forms you can leave the field editable and users can both type and copy. Be explicit about which mode each field is in — mixing editable and readonly fields in one panel is the most common source of "why didn't my value save?" bug reports.
Virtual / accessor-backed values. If the copyable value isn't a column, either use an accessor on the model (and $appends) or supply it via resolveUsing(). Verify the resolved value, not the displayed one, is what lands on the clipboard.
Consistent action panels. Group copy fields with ->help() text and Nova 4 Heroicons-styled buttons now look native; if you maintain screenshots or visual regression tests, regenerate them after upgrading (see Gotchas).
Extension point: subclass TextCopy and override component() if you need different button markup, a toast instead of the default feedback, or an aria-label. There's no published view to edit in place — treat the Vue component as vendor code.
null or '', not a broken asset build. Use it as a diagnostic — check accessors, eager-loaded relations, and resolveUsing closures first.navigator.clipboard is only available over HTTPS or on localhost. A plain http://myapp.test or an IP-based staging URL will silently fail to copy. Run valet secure / herd secure locally, and confirm staging is HTTPS before filing a bug.->rules('required') on a readonly() field can produce confusing validation behaviour because the value isn't submitted. Put validation on the field that actually writes the column.->displayUsing(), confirm which string reaches the clipboard — truncation or formatting applied for display may carry through. Test with a long token rather than assuming.hideFromIndex() if the button crowds the table.composer show and click a copy button in each Nova version you support rather than trusting semver.php artisan nova:publish --force or your asset build), then check the browser console — clipboard failures surface there as rejected promises, not PHP errors.How can I help you explore Laravel packages today?