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

Nova Vimeo Field Laravel Package

tanjemark/nova-vimeo-field

A Laravel Nova field for adding Vimeo videos to your resources. Store Vimeo URLs or IDs, preview videos in Nova forms and detail views, and keep video handling simple with a configurable field that fits your existing resource workflow.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with a pin, not a caret.

composer require tanjemark/nova-vimeo-field:*
composer show tanjemark/nova-vimeo-field   # note the exact version, then pin it

Change composer.json to the resolved exact version or the release tag. A caret constraint against a package whose last release was 2021-02-21 resolves to "whatever exists forever" and gives you no signal when composer update re-resolves it. Then commit the lock file diff.

Read vendor/tanjemark/nova-vimeo-field/ before writing a single line of resource code. The README is not the contract; the source is. Confirm four things in order:

  1. The field class namespace and constructor signature. Likely Tanjemark\NovaVimeoField\VimeoField, but verify — the make() argument order (make($name, $attribute)) must match what you assume before you paste it into ten resources.
  2. The laravel/nova constraint in the package's composer.json. This is the compatibility oracle. A 2021 package is Nova 3-era even if the marketing copy says Nova 4.
  3. Whether dist/ is committed. If there is no built JS in the package, the field will render as an empty box until you run the Nova asset toolchain against it. If dist/ exists, note its file dates — stale assets relative to src/ produce a field that saves correctly but previews wrongly.
  4. Where Vimeo is actually called. Search src/ and dist/js/ for vimeo.com, api.vimeo.com, oembed, and token. This single grep tells you whether the field embeds an iframe from a stored ID (low risk) or performs an authenticated API lookup (a real integration surface with credential requirements).

The canonical first use case — one nullable string column holding a Vimeo reference:

// migration
$table->string('vimeo_url')->nullable();

// app/Nova/Video.php
use Tanjemark\NovaVimeoField\VimeoField; // verify against vendor/

public function fields(Request $request)
{
    return [
        ID::make()->sortable(),
        Text::make('Title'),
        VimeoField::make('Vimeo', 'vimeo_url')
            ->rules('nullable', 'url'),
    ];
}

Then run the two-video test, which is the most valuable 10 minutes you will spend on this package:

  1. Paste a public Vimeo URL. Save. Reload the edit form and the detail view. Confirm the thumbnail renders in both.
  2. Repeat with a private or unlisted video (unlisted Vimeo links carry a ?h=<hash> parameter — test one of those specifically).

Public working / private broken is the signature of a field that only builds an unauthenticated oEmbed iframe. Both working means the embed path is robust enough to trust. Both broken means stop here and read the debugging sequence in Gotchas before touching your resource code.

Implementation Patterns

Decide URL-vs-ID on day one and never revisit it. The package advertises support for both; your schema should be opinionated. A full URL (https://vimeo.com/123456789) is self-describing, human-readable in the database, and survives a swap to any other embed mechanism. A bare ID is compact and easier to interpolate into a player URL but is meaningless on its own. Pick one, enforce it with a cast or mutator, and document it in the column comment.

// app/Models/Video.php
protected function vimeoUrl(): Attribute
{
    return Attribute::make(
        set: fn (?string $value) => $value === null
            ? null
            : 'https://vimeo.com/' . preg_replace('/\D/', '', basename(parse_url($value, PHP_URL_PATH))),
    );
}

Whatever the field happens to hand you, the model writes one canonical shape. That is the property that makes the dependency replaceable.

Wrap the field in a factory method. This is the highest-leverage pattern in the entire package — Nova fields are cheap to instantiate and expensive to find-and-replace across resource classes:

final class Fields
{
    public static function vimeo(string $attribute = 'vimeo_url'): Field
    {
        return VimeoField::make('Vimeo', $attribute)
            ->rules('nullable', 'url')
            ->hideFromIndex();
    }
}

Every resource calls Fields::vimeo(). Swapping to a first-party Text + oEmbed field later becomes a one-file change instead of a repo-wide grep. Adopt this even if you are certain you will keep the package.

Treat the field as a display concern, never a data-integrity boundary. The package stores whatever string it is handed. Validation belongs in ->rules(), normalization belongs in the model mutator, and nothing derived — thumbnail URL, title, duration — ever gets persisted from the field. If the field's internals break, your database is still valid and your public site still renders.

Keep public rendering completely independent. Resolve the stored value on the front end with your own oEmbed call or a Vimeo API v3 request using a token from .env. Never let the admin field be the source of truth for playback. This decouples editor UX from runtime rendering, so a broken admin panel — or a deleted upstream package — never takes down a customer-facing page.

Use standard Nova field modifiers rather than waiting for package-specific config. ->hideFromIndex(), ->onlyOnDetail(), ->sortable() (if the column is a plain string), ->readonly(), ->nullable() all work on any Nova Field subclass. You rarely need the package's own display options; the Nova API covers most day-to-day layout needs, and using it keeps your resources portable.

Test without the field. Seeder and factory data should write raw strings into the column, never go through Nova:

Video::factory()->create(['vimeo_url' => 'https://vimeo.com/76979871']);

If your tests need Nova to construct a model, your abstraction is leaking.

Have a fork plan before you need it. If the package breaks on a Nova upgrade, the fallback is roughly a day of work: copy the field class and Vue component into app/Nova/Fields/VimeoField.php and nova-components/, register the assets yourself, and keep the exact same public API so Fields::vimeo() does not change. The MIT license makes this legally trivial; only the calendar time is real.

Know the workflow boundary. This is a paste-a-reference affordance. Library browsing, uploads, captions, domain-restricted embeds, and metadata sync are separate integrations. Do not attempt to coax them out of a field that stores a string.

Gotchas and Tips

Version coupling is the dominant risk, and it is not theoretical. Nova 4 and Nova 5 changed field internals, the laravel-nova JS mixin surface, and asset build tooling. A 2021 release has almost certainly not been validated against either. Spike it in a scratch Nova app on your installed major before it enters a shared environment, and do not accept a passing README as evidence.

Missing or stale dist/ is the number-one cause of "the field is broken." If assets are not committed, the PHP side registers fine and the Vue component never mounts — you get a labelled empty box that still saves. If assets are committed but predate the field class, you get a preview that renders the wrong thing. Check file timestamps in dist/js/ against src/ before filing a bug against your resource code.

Content-Security-Policy will silently kill the preview. The preview is an iframe. A strict Content-Security-Policy on admin routes produces an empty box and an editor saying "the field is broken." Check the browser console for frame-src and frame-ancestors violations first — this is a 30-second diagnosis that people routinely spend two hours missing.

Unlisted videos are the edge case that bites. Vimeo private/unlisted links include a hash (https://vimeo.com/123456789/abcdef1234 or a ?h= query param). If your mutator strips everything but the numeric ID, you silently break every unlisted video on the site. Test the regex against a real unlisted URL before shipping normalization.

->rules('url') contradicts storing a bare ID. If you choose ID storage, url validation rejects every valid value. Use ->rules('nullable', 'string', 'regex:/^\d{6,12}$/') instead, and make sure the field's own picker output matches. Mismatched validation and storage format is the most common self-inflicted wound with this package.

The Vimeo auth path is an empirical question. Vimeo deprecated unauthenticated API access in favour of token-authenticated v3. If the field calls the API without a token, it may already be broken at install. If it only renders an oEmbed iframe from a stored ID, the risk is far lower. The private-video test in Getting Started is how you distinguish "dead on arrival" from "works fine" without reading minified JS.

Expect silent failures, and design so they stay bounded. Token expiry, deprecated endpoints, and deleted videos all degrade to a missing thumbnail — never data loss, never a failed save. That bounding is the package's best property. It only holds if you keep public rendering independent (see Implementation Patterns).

Do not put it on a critical path. Eight stars, zero dependents, no release since early 2021: there is no upstream to patch a Nova break and no community knowledge base to search. If you adopt it, name an owner in your dependency register, otherwise "unmaintained and unscheduled" is the default state.

Debugging sequence — work it in this order:

  1. Browser console for CSP violations and JS errors.
  2. Presence and freshness of dist/.
  3. Public vs. private video on the same resource.
  4. The raw stored string in the database.
  5. The actual API/embed call site in src/.

This order avoids the classic trap of rewriting resource definitions when the real fault is an iframe blocked by CSP.

Budget the alternative honestly. If the requirement is genuinely "paste a Vimeo URL and see a preview," a Nova Text field with resolveUsing/displayUsing for an oEmbed thumbnail covers ~80% of the value in a few hours with zero third-party surface. Spend a half-day on the package spike and a half-day on the first-party build, compare like for like, and keep whichever wins behind Fields::vimeo() so the decision stays reversible.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package