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 Iframe Popup Laravel Package

norman-huth/nova-iframe-popup

A Laravel Nova component for opening URLs in an iframe popup/modal, letting you embed external or internal pages without leaving the admin panel. Useful for quick previews and integrations, though adoption is limited.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and publish

composer require norman-huth/nova-iframe-popup
php artisan vendor:publish --provider="NormanHuth\NovaIframePopup\FieldServiceProvider" --tag=assets

The package ships compiled JS/CSS that register a custom Nova field component, so the publish step is mandatory — a missing publish is the #1 cause of a blank/unrendered column. Use --force after upgrades.

2. Find the real API before trusting the README

The README is from 2022 and the repo is archived. Open src/ first and read the field class — confirm the exact FQCN, the URL setter, and which fluent methods exist (withMeta, sizing, button label). Treat anything the README shows as a hint, not a contract; verify against the source before writing code you can't easily rewrite.

3. Drop it into a Resource's fields()

use NormanHuth\NovaIframePopup\NovaIframePopup;

public function fields(NovaRequest $request): array
{
    return [
        // ... normal fields
        NovaIframePopup::make('Preview')
            ->url(fn () => route('admin.preview', $this->resource))
            ->hideFromIndex()
            ->hideWhenCreating()
            ->hideWhenUpdating(),
    ];
}

4. First real use case: a detail-page "open tool" button

The highest-value, lowest-risk use is a detail-only button that opens a same-origin Laravel route in a modal iframe — a legacy admin panel, a document preview, a mail-log viewer, or a Stripe/Grafana dashboard you've wrapped in a thin controller. Keep it detail-only: iframe fields in index render as a broken column and in forms they add noise.

Implementation Patterns

Back the iframe with an app-owned route, not a third-party URL

The reliable pattern is: Nova field → route('admin.preview', $this) → your controller → a Blade view with a minimal layout (no Nova assets, no auth middleware beyond what you need). This gives you three wins for free: same-origin cookies work, you control headers, and you can inject the resource context.

// routes/web.php
Route::middleware(['web', 'auth', 'can:view,order'])
    ->get('/admin/orders/{order}/preview', OrderPreviewController::class)
    ->name('admin.preview');
// OrderPreviewController
public function __invoke(Order $order)
{
    return response()
        ->view('admin.orders.preview', compact('order'))
        ->header('X-Frame-Options', 'SAMEORIGIN');
}

Pass context through query strings, not through JS

Because the modal body is a fresh document, don't try to share Nova's Vuex state. Serialize what you need into the URL from a closure — it's evaluated server-side per request, so $this is the underlying model:

NovaIframePopup::make('Customer')
    ->url(fn () => route('admin.crm.embed', [
        'email' => $this->email,
        'tab'   => 'orders',
    ]))

URL-encode nothing manually; route() handles it. This is also the cleanest way to make the embed deep-linkable so you can open the same URL standalone when debugging.

Wrap third-party tools with a proxy controller

For anything you don't control (Grafana, Metabase, an old Rails admin), a thin proxy route that authenticates to the upstream and streams the HTML with permissive frame-ancestors is usually faster than fighting the vendor's headers. Keep it read-only and scoped to one resource — never build a general-purpose proxy.

Handle "open in new tab" as an escape hatch

Embedded UIs inevitably need a full-page mode. Two options that work:

  • In the wrapped view, make links that shouldn't be trapped use target="_top".
  • Expose a "Open full page" link in the modal header if the field supports slots/meta; otherwise put it inside your wrapped Blade as a fixed-position anchor.

Let the child talk to Nova via postMessage

If you need the modal to close or the parent to refresh after a child action (e.g. "Saved — reload record"), have the wrapped page post a message and listen for it. Always check event.origin against your own host; a compromised embed turning into an XSS pivot on the Nova session is a real risk.

Migration path if the package breaks on Nova 5+

It's archived with zero dependents. If you need to move, a hand-rolled custom field is ~60 lines: a Field subclass, ->component('iframe-popup'), a resources/js/field.js that renders Modal + <iframe>. Budget that rather than waiting for a fork to appear.

Gotchas and Tips

Blank modal = headers, not the package. Before touching PHP, open DevTools → Network → the iframe document request and check:

  • X-Frame-Options: DENY or SAMEORIGIN when you're cross-origin
  • Content-Security-Policy: frame-ancestors 'none' (Grafana, Metabase, and most SaaS default to this)
  • Content-Security-Policy: frame-src on your app if you run a CSP middleware — Nova ships without one, but many apps add it and it silently blocks the iframe

SameSite cookies break SSO inside iframes. If the embedded app authenticates against your Laravel session and the parent page is a different site, SameSite=Lax cookies are not sent and you get an infinite login redirect. Fix by keeping the embed same-origin, or set SameSite=None; Secure on the session cookie — and understand the trade-off before you do. Also add the iframe's host to SANCTUM_STATEFUL_DOMAINS / SESSION_DOMAIN if auth straddles subdomains.

Hide aggressively. An iframe field rendered in index is a broken column and in create/update forms it's dead weight. Always pair with ->hideFromIndex()->hideWhenCreating(); decide deliberately about hideWhenUpdating() — the modal is sometimes more useful on update, where the user has context.

Nests of iframes. If Nova itself lives in an iframe (some portals do this), the modal's iframe is second-level. window.top references and postMessage chains get confusing, and some browsers' third-party cookie policies differ. Verify in the actual deployment topology, not just php artisan serve.

Asset pipeline drift. A 2022 package was built against Nova 3/4's Mix-era asset setup. On newer Nova the publishing tag may differ, nova:publish may not pick it up, and the compiled JS may reference renamed Nova components. Symptom: the field renders nothing or the modal fails to mount. Check nova-components conventions in your installed version and, if needed, copy the JS into resources/js and rebuild.

Don't drive navigation from inside the iframe. Any full-page redirect inside the modal loads your Nova shell inside the modal shell — recursive chrome, broken close button, doubled nav bars. Audit the wrapped view's links; use target="_top" for anything that isn't meant to stay inside.

Fluent-closure evaluation. ->url(fn () => ...) runs on every detail request, including ones you don't see (breadcrumb prefetches, index polling if you forgot hideFromIndex). Keep the closure cheap — no HTTP calls, no heavy queries; if it's expensive, cache it.

Security posture. An iframe is a same-session window into your app. Authorise in the controller (can:view,$model), add sandbox attributes if the field exposes them, and never render a URL derived from user-controlled input without an allowlist of hosts. The modal's implicit trust of its own origin is exactly what makes a misconfigured embed dangerous.

Pin or fork. Archived, 6 stars, zero dependents, last release April 2022 — composer won't tell you when it silently breaks. Commit a version constraint, and add a smoke test (visit a detail page, assert the iframe src) so an upgrade that kills it fails loudly in CI rather than in front of a user.

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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle