metrixinfo/nova-iframe
Easily embed external or internal web pages inside Laravel Nova with this simple iframe component. Add an iframe field to resources or a dashboard card, configure the URL, height, width, and other display options, then render trusted content alongside your admin data.
Architecture fit. The package is a thin Nova field that renders an iframe — roughly 50 lines of PHP plus a compiled Vue asset. It sits entirely in the presentation layer of the Nova admin: no migrations, no service providers with side effects, no data model, no queue or event surface. That is a good architectural shape for what it does. It also means the package is not an integration layer — it cannot propagate sessions, authenticate against the target, transform data, or handle write-back. Any workflow that needs those things must be built outside the package and will not be helped by adopting it.
Fit should be judged against the workflow, not the code: does embedding a read-only dashboard, provider console, status page, or PDF preview inside a resource detail view measurably reduce context switching for operators? The iframe is incidental; the consolidation bet is the real decision.
Integration feasibility. Low-effort and additive. The realistic path is: read vendor/metrixinfo/nova-iframe/composer.json to confirm the laravel/nova constraint matches our Nova major, verify the actual class name/namespace in src/ (micro-package READMEs are routinely stale on casing), then add a single field to one resource's fields(). Rollback is deleting one line. Nothing here touches the database, so there is no migration or data-integrity risk.
The real feasibility gate is external and non-negotiable: the target origin must permit framing. curl -sI <target> | grep -iE 'x-frame-options|content-security-policy' returns DENY or SAMEORIGIN, the package cannot help, and no amount of Nova work will fix it. Run that check before scheduling engineering work. The second gate is our own CSP frame-src allowlist — a deployment-config change that must land in the same PR or the feature will appear broken.
Technical risk. Concentrated, small, and mostly outside the package:
new Vue / Vue.component; if present, it will not register on Nova 4+ and the inline rebuild is the correct answer.sandbox, allow, and referrerpolicy are likely unset. If the URL derives from record data, an unescaped src binding is an injection vector. These are our review deliverables regardless of vendor.http:// content silently blocked in an HTTPS admin; javascript:/data: schemes if URLs come from user-supplied slugs.Key questions to resolve before commitment:
laravel/nova constraint actually admit it?frame-src CSP change?Stack fit. Requires an existing Laravel + Nova admin. It is a presentation-only dependency with no runtime footprint in the application proper. It fits cleanly if and only if the use case is display-only: resource detail views, or a dashboard card. It explicitly does not fit create/update form interactivity — iframes serialize nothing into form state — nor SSO/session propagation, deep-linked authenticated tools, or high-availability operator surfaces. Route those to API or JS integrations.
Migration path. There is no migration. The package is additive, and the reversal is a one-line deletion from fields(). The migration work that does exist is around it: adding the origin to frame-src, deciding an allowlist policy, and building URL construction on the model rather than in fields() so it is testable and can fail closed. Treat the package as a scaffold for a first-party field rather than a long-term dependency; the honest framing is "borrow the reference implementation, own the outcome."
Compatibility. Two compatibility axes, both cheap to check and both blocking:
composer.json first; five seconds tells you whether the field registers at all. Then confirm the compiled bundle is not Vue 2 if we are on Nova 4+.X-Frame-Options, target frame-ancestors, and our own frame-src all must align. This is deployment configuration, not package configuration, and it is the most common source of "the package is broken" tickets when the package is fine.The realistic long-term compatibility plan is a fork. Extending the field and swapping the Vue component (component(): string returning our own registered component) is a one-to-two-day job because the underlying package is ~50 lines. Budget that up front rather than discovering it during a Nova upgrade.
Sequencing.
curl the target headers; read composer.json; grep the bundle for sandbox/allow/url/height and for Vue 2 idioms. Output: go / no-go / fork-now.frame-src allowlist, sandbox attributes, allowlist validation in the URL builder. This lands with the feature, not after.onlyOnDetail() + hideFromIndex(), behind a feature flag. Visible fallback link shipped in the same change.composer.lock, add one feature/Dusk test asserting the detail page contains the expected src, name an owner in an ADR.Maintenance. Low absolute effort, non-zero and lumpy. Budget roughly half a day per Nova major for re-validation, plus whatever the CSP allowlist review costs annually. The single-maintainer, 7-star profile means we should assume we are the maintenance path: pin the exact constraint (1.0.* or tighter), commit composer.lock, and treat any upstream commit as unreviewed until read. If the package doesn't auto-register its compiled asset, register it ourselves via Nova::serving() — that is a small piece of glue we now own permanently.
Support. The support surface is operator-facing and predictable, and most of it is not the package's fault. Expect "blank box" reports. Triage in a fixed order: devtools console for X-Frame-Options / frame-src violations, network tab to confirm the request was issued at all, then curl -sI on the target. Publish that triage as a runbook so tier-one doesn't escalate to engineering. Also publish the escape hatch: an embed failure must never block an operator from completing the task, so every embed ships with a visible "open in new tab" affordance.
Scaling. Two distinct scaling concerns:
onlyOnDetail() + hideFromIndex() as a code-review rule, not a preference. If we embed in a custom tool that reuses component instances across records, bind :key to the record ID so frames remount instead of showing stale content.Failure modes. Ranked by likelihood and cost:
| Mode | Symptom | Mitigation |
|---|---|---|
| Target refuses framing | Blank frame, console CSP/X-Frame-Options error |
Pre-flight curl; fall back to link-out |
| Our CSP blocks origin | Blank frame after deploy | Ship frame-src change in same PR; document owner |
| Nova major upgrade | Field renders nothing, unknown-component error | Pin; add one Dusk test as the five-minute upgrade signal |
| Mixed content | Silently blocked | Normalise to https, fail loudly |
| Unsafe URL from record data | Injection vector | FILTER_VALIDATE_URL + host allowlist server-side, fail closed |
| Cookie/session not propagating | Auth wall inside frame | Short-lived signed URL, or don't frame authenticated tools |
| Overloaded index page | Slow admin | Review rule: detail views only |
Ramp-up. Effectively zero for a Laravel developer — one field declaration. The learning is not the API; it is the operating constraints: iframes don't participate in form state, don't carry the Nova session cross-origin, and are governed by CSP and X-Frame-Options at both ends. Onboarding should cover the CSP allowlist policy, the blank-frame triage order, the detail-view-only rule, and the sandbox attribute policy (never allow-scripts + allow-same-origin together on untrusted origins). One named owner, one ADR, one test, one runbook — that is the entire operational package, and it outlives the dependency.
How can I help you explore Laravel packages today?