NovaCard HTML

[ A Laravel Nova card ]

HTML on a card.

Nova gives you metrics for numbers and a Vue SDK for everything else. This is the middle: markup your application already produces, placed on the dashboard grid, with lazy loading, caching and dark mode handled for you.

Four content sources. Twelve presentation methods. Five performance methods. One trust boundary, documented, with an opt-in sanitizer on the safe side of it.

Get it on Packagist See it work composer require gabrielesbaiz/nova-card-html

Say it once

Override content(), or build it fluently at the call site. Both are first-class; the subclass hook has never changed.

Show it late

lazy() keeps queries off the critical path. #[LazyState] carries a selected year across the fetch.

Trust it never

sanitize() for anything a user touched. text() escapes. Markdown strips HTML unless you say otherwise.

Ship it small

4.95 kB of JS, 2.23 kB of CSS, both gzipped under 2 kB. No runtime dependency of its own.

On the dashboard

Six cards.
None of them in the way.

Every card here is ->lazy(). The dashboard shell paints first, then each card fetches its own content through Nova's own middleware group. Add ->cache() and a hit costs one small request instead of a query.

dashboard · main
width 1/3
active users
1,284+6%
width 1/3
orders
312today
width 1/3 · spark
conversion
width 1/2 · markdown()
release notes
3.0.0 — lazy loadingnew
2.2.1 — generator fixpatch
width 1/2 · view()
signups by year
2024418
2025602
width full · lazy() · refreshEvery(15)
conversion funnel · live
skeleton → fetching…

Sheet 01 · card geometry

Cards, to spec.

Nova lays its dashboard out on twelve columns. A card declares how many it takes, and this package honours every one of them — including width('full'), which Nova itself forces to a dynamic height.

1/43 cols
width('1/4')
Orders today
25%
1/34 cols
width('1/3') · the default
Active users
33.3%
1/26 cols
width('1/2') · markdown()
Release notes · markdown
50%
2/38 cols
width('2/3') · view()
Signups by year · blade view
66.6%
Full12 cols
width('full') · forces dynamic height
Conversion funnel · lazy
100%

Sheet 02 · every option except width

Set it. See it.

Every presentation and behaviour method on one card, live. Width has its own sheet above; everything else is here. The call on the right rewrites itself as you click, listing only the options you actually switched on.

HEIGHT
ALIGN
THEME
CHROME
BEHAVIOUR
Conversion funnel ▾
Q1 · 2026Export
62.4%
Trial to paid, rolling 30 days.
Visitors1,284
Signups1,002
Activated861
Trialing640
Converted512
Renewed481
Upgraded402
Churned77
Updated 2 minutes ago

Grows with its content — no scrollbar.


      

Usage

PHP goes in.
A card comes out.

Four content sources, one card. Closures resolve through the container, so they can type-hint NovaRequest.

app/Nova/Cards/Funnel.php

      
rendered · nova dashboard

Conversion funnel

62.4%

Trial → paid, rolling 30 days.

Performance

Stop blocking the paint.

A dashboard of HTML cards runs every card's queries before the page renders, because serialization happens inside the dashboard request. Four methods move that work off the critical path.

eagerevery card queries firstfirst paint 1,840ms
6 cards × queries · 1,510ms
paint
->lazy()shell first, cards afterfirst paint 210ms
paint
cards fetch in parallel
->lazy()->cache()warmfirst paint 210ms
paint
cache hit
0ms500ms1000ms1500ms2000ms
0ms

Illustrative timings for a six-card dashboard. The shape is the point: the shell no longer waits for the slowest query on the page.

->cache($ttl)->lazy() ->deferUntilVisible()->refreshEvery(15) #[LazyState]