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 Html Card Laravel Package

abordage/nova-html-card

A lightweight Laravel Nova card for rendering custom HTML on dashboards and resource pages. Add notices, links, embeds, metrics, or any markup without building a custom card from scratch—simple configuration and works anywhere Nova cards are supported.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install into the same app as Nova (Nova 4/5 assumed — check the README's compatibility table before committing):

    composer require abordage/nova-html-card
    

    The package ships pre-compiled JS in dist/, so there is no npm install/npm run dev step in your app.

  2. Confirm the exact class FQCN before writing code — this is a 12-star package, so copy it from the README or vendor/abordage/nova-html-card/src/ rather than trusting memory. It will be a Laravel\Nova\Card subclass (a name like Abordage\NovaHtmlCard\HtmlCard) exposing a fluent setter such as ->html(...).

  3. Register it on a dashboard in app/Providers/NovaServiceProvider.php:

    public function cards(): array
    {
        return [
            (new HtmlCard)->html('<p class="text-green-500">All systems operational.</p>'),
        ];
    }
    
  4. php artisan nova:publish if the README asks for it, then hard-refresh. If the card renders but appears empty, jump straight to the Gotchas section — that is almost always an escaping or asset-manifest issue rather than a registration problem.

First real use case: a dashboard card with a hand-written "quick links" or "on-call rota" block. It is the fastest possible Nova card: no Vue, no build, no API endpoint. Only reach for ->html() on dashboards you control, not on content that comes from users.

Implementation Patterns

Render Blade, don't concatenate strings. The package's value proposition collapses the moment you start building HTML with sprintf. Keep markup in a Blade partial and pass the rendered string:

(new HtmlCard)
    ->html(view('nova.cards.system-status', [
        'failed' => Queue::failedCount(),
        'jobs'   => Queue::size(),
    ])->render())
    ->width('1/2'),

This gives you syntax highlighting, @foreach, and @lang for free, and keeps the PHP side down to a data query.

Small reports. Compute the numbers in the provider or a dedicated DashboardMetrics class, render a tiny table, and let Nova's layout handle placement. Cap the query cost — see Gotchas.

Notification/activity lists. Query the last N records and render an <ul>. Always e() (or {{ }} inside the Blade partial) every user-derived field; you are rendering into v-html, so Blade's auto-escaping is your only line of defence.

Resource index cards. For cards attached via Resource::cards(), you typically have no model instance. Read the request context (NovaRequest, $request->resourceId, $request->viaResource) if you need to scope the content, and return empty/neutral HTML on the index rather than throwing.

Gate and scope. Because content is computed server-side, canSee() is a genuine security boundary, not cosmetics:

->canSee(fn ($request) => $request->user()->hasRole('admin'))

Use it for anything financial, PII, or internal-ops flavoured, and never rely on hiding it client-side.

Composition. If the block needs to be interactive (filters, accordions, live polling), this package is the wrong tool — write a proper Vue card or a Nova custom tool. Use HTML Card for read-only snapshots.

Version control the Blade partial alongside the provider so the card has a reviewable diff trail; the card class itself is throwaway glue.

Gotchas and Tips

  • It's a server-rendered snapshot. Nova fetches cards once on page load. The HTML will not react to anything — no Livewire, no polling, no @click. If the README exposes a refresh button/meta, use it; otherwise document that users must reload the page.
  • ->html() bypasses escaping by design. Any interpolation of $user->name, a log line, or an exception message is an XSS vector. Sanitise or escape explicitly — {!! !!} habits from Blade do not transfer safely here.
  • Mustache/Vue syntax is inert. v-html does not compile {{ }} or @click attributes, so half-broken Alpine snippets will silently no-op. That is a feature, but it confuses people.
  • Blade in a PHP string doesn't render. ->html('<x-badge>') outputs the literal tag. Either view(...)->render() or Blade::render(...).
  • Payload weight. The HTML is serialised into the Nova cards API response. A 200-row table on every dashboard load adds latency to a request that also builds every other card. Aggregate in SQL (count, group by, limit), never ->get() into the view.
  • Styling isolation. Nova's Tailwind is compiled and scoped to its own build. Utility classes you think exist may not be in Nova's bundle, and hardcoded hex colours break under Nova's dark mode. Safest options: reuse classes you can verify in Nova's UI, or ship two or three custom CSS rules via Nova::style() keyed off Nova's dark-mode selector.
  • Width API drift. Nova changed width() semantics between majors. If the card is mis-sized, check the Nova version, not the package.
  • Stale assets after upgrade. Because dist/ is committed, an upgrade can require a browser cache bust; run nova:publish and hard-refresh before filing a bug against your own code.
  • Testability. The card JSON is awkward to assert. Extract the data-building method into a plain class and test that; a single assertStringContainsString on the rendered partial covers the rest.
  • Bus factor. 12 stars, zero dependents, single maintainer, last release Jan 2025. The surface area is tiny (~one card class plus a component), so the risk is low, but pin the version in composer.json and be prepared to vendor the two files if it goes unmaintained — that is cheaper than migrating a dozen dashboards later.
  • Alternatives worth knowing: Nova's built-in Help card, a plain Nova::script/Nova::style injection, or a 40-line custom card. If you find yourself adding a second HtmlCard subclass, you have outgrown the package.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin